Skip to main content

retex_engine/
nodes.rs

1//! Node memory: an arena of typesetting nodes addressed by [`NodeRef`].
2//!
3//! The node model mirrors LuaTeX's. Node type ids, subtypes and field sets
4//! are those that LuaTeX's `node` library reports (see
5//! `data/luatex-node-types.txt`). Lists are doubly linked through `next` and
6//! `prev`. List heads in the semantic nest are `temp` nodes, as in TeX.
7
8use std::rc::Rc;
9
10use crate::glue::Glue;
11use crate::token::TokenList;
12
13/// LuaTeX's node types, fields and subtypes, as `node.types()`,
14/// `node.fields()` and `node.subtypes()` report them
15/// (`data/luatex-node-types.txt`, see `data/README.md`).
16pub const LUATEX_NODE_TYPES: &str = include_str!("../data/luatex-node-types.txt");
17
18/// The Lua name of node type `t` (`glue`), from [`LUATEX_NODE_TYPES`].
19pub fn type_name(t: u8) -> String {
20    LUATEX_NODE_TYPES
21        .lines()
22        .filter_map(|l| l.strip_prefix("type "))
23        .find_map(|l| {
24            let mut it = l.split_whitespace();
25            let n: u8 = it.next()?.parse().ok()?;
26            (n == t).then(|| it.next().unwrap_or("").to_owned())
27        })
28        .unwrap_or_default()
29}
30
31/// A reference to a node in the node arena; `NodeRef::NULL` is the empty
32/// list.
33#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, PartialOrd, Ord)]
34pub struct NodeRef(pub u32);
35
36impl NodeRef {
37    /// The null pointer.
38    pub const NULL: NodeRef = NodeRef(0);
39
40    /// Whether this is the null pointer.
41    #[inline]
42    pub const fn is_null(self) -> bool {
43        self.0 == 0
44    }
45}
46
47/// A "running" rule dimension (TeX's `null_flag`, −2³⁰).
48pub const RUNNING: i32 = -0o10000000000;
49
50/// Node type ids, numbered as LuaTeX's `node.types()`.
51#[allow(missing_docs)]
52pub mod id {
53    pub const HLIST: u8 = 0;
54    pub const VLIST: u8 = 1;
55    pub const RULE: u8 = 2;
56    pub const INS: u8 = 3;
57    pub const MARK: u8 = 4;
58    pub const ADJUST: u8 = 5;
59    pub const BOUNDARY: u8 = 6;
60    pub const DISC: u8 = 7;
61    pub const WHATSIT: u8 = 8;
62    pub const LOCAL_PAR: u8 = 9;
63    pub const DIR: u8 = 10;
64    pub const MATH: u8 = 11;
65    pub const GLUE: u8 = 12;
66    pub const KERN: u8 = 13;
67    pub const PENALTY: u8 = 14;
68    pub const UNSET: u8 = 15;
69    pub const STYLE: u8 = 16;
70    pub const CHOICE: u8 = 17;
71    pub const NOAD: u8 = 18;
72    pub const RADICAL: u8 = 19;
73    pub const FRACTION: u8 = 20;
74    pub const ACCENT: u8 = 21;
75    pub const FENCE: u8 = 22;
76    pub const MATH_CHAR: u8 = 23;
77    pub const SUB_BOX: u8 = 24;
78    pub const SUB_MLIST: u8 = 25;
79    pub const MATH_TEXT_CHAR: u8 = 26;
80    pub const DELIM: u8 = 27;
81    pub const MARGIN_KERN: u8 = 28;
82    pub const GLYPH: u8 = 29;
83    pub const ALIGN_RECORD: u8 = 30;
84    pub const PAGE_INSERT: u8 = 33;
85    pub const SPLIT_INSERT: u8 = 34;
86    /// A glue specification made by Lua code (`node.new("glue_spec")`);
87    /// its data is that of a glue node.
88    pub const GLUE_SPEC: u8 = 39;
89    pub const TEMP: u8 = 41;
90    /// A freed node (in the arena's free list).
91    pub const FREE: u8 = 255;
92}
93
94/// The deepest nesting of boxes that is copied and shipped out (TeX
95/// builds at most 1000 levels at a time, LuaTeX ships out 20000 boxes
96/// nested in each other, and Lua code can make a box contain itself).
97pub const MAX_NESTING: u32 = 50_000;
98
99/// Glue subtypes (LuaTeX numbering).
100#[allow(missing_docs)]
101pub mod glue_sub {
102    pub const USER: u16 = 0;
103    pub const LINE_SKIP: u16 = 1;
104    pub const BASELINE_SKIP: u16 = 2;
105    pub const PAR_SKIP: u16 = 3;
106    pub const ABOVE_DISPLAY: u16 = 4;
107    pub const BELOW_DISPLAY: u16 = 5;
108    pub const ABOVE_DISPLAY_SHORT: u16 = 6;
109    pub const BELOW_DISPLAY_SHORT: u16 = 7;
110    pub const LEFT_SKIP: u16 = 8;
111    pub const RIGHT_SKIP: u16 = 9;
112    pub const TOP_SKIP: u16 = 10;
113    pub const SPLIT_TOP_SKIP: u16 = 11;
114    pub const TAB_SKIP: u16 = 12;
115    pub const SPACE_SKIP: u16 = 13;
116    pub const XSPACE_SKIP: u16 = 14;
117    pub const PAR_FILL_SKIP: u16 = 15;
118    pub const MATH_SKIP: u16 = 16;
119    pub const THIN_MU_SKIP: u16 = 17;
120    pub const MED_MU_SKIP: u16 = 18;
121    pub const THICK_MU_SKIP: u16 = 19;
122    pub const COND_MATH_GLUE: u16 = 98;
123    pub const MU_GLUE: u16 = 99;
124    pub const A_LEADERS: u16 = 100;
125    pub const C_LEADERS: u16 = 101;
126    pub const X_LEADERS: u16 = 102;
127    pub const G_LEADERS: u16 = 103;
128}
129
130/// Subtypes of hlist and vlist nodes that math typesetting gives the boxes
131/// it builds (LuaTeX's `mathchar`, `vextensible`, `vdelimiter`).
132pub mod list_sub {
133    /// A box holding one math character.
134    pub const MATH_CHAR: u16 = 9;
135    /// A horizontal extensible delimiter.
136    pub const H_EXTENSIBLE: u16 = 10;
137    /// A vertical extensible delimiter.
138    pub const V_EXTENSIBLE: u16 = 11;
139    /// A null (empty) delimiter.
140    pub const V_DELIMITER: u16 = 13;
141    /// A delimiter above its nucleus (`\Uoverdelimiter`, `\Udelimiterover`).
142    pub const OVER_DELIMITER: u16 = 14;
143    /// A delimiter below its nucleus.
144    pub const UNDER_DELIMITER: u16 = 15;
145    /// The numerator of a fraction.
146    pub const NUMERATOR: u16 = 16;
147    /// The denominator of a fraction.
148    pub const DENOMINATOR: u16 = 17;
149    /// An operator with limits.
150    pub const LIMITS: u16 = 18;
151    /// A fraction.
152    pub const FRACTION: u16 = 19;
153    /// The nucleus of a noad.
154    pub const NUCLEUS: u16 = 20;
155    /// A superscript.
156    pub const SUP: u16 = 21;
157    /// A subscript.
158    pub const SUB: u16 = 22;
159    /// The degree of a radical.
160    pub const DEGREE: u16 = 23;
161    /// A superscript and a subscript.
162    pub const SCRIPTS: u16 = 24;
163    /// An overline.
164    pub const OVER: u16 = 25;
165    /// An underline.
166    pub const UNDER: u16 = 26;
167    /// An accented nucleus.
168    pub const ACCENT: u16 = 27;
169    /// A radical.
170    pub const RADICAL: u16 = 28;
171}
172
173/// Kern subtypes.
174#[allow(missing_docs)]
175pub mod kern_sub {
176    pub const FONT: u16 = 0;
177    pub const USER: u16 = 1;
178    pub const ACCENT: u16 = 2;
179    pub const ITALIC: u16 = 3;
180}
181
182/// Glyph subtypes (bit flags).
183#[allow(missing_docs)]
184pub mod glyph_sub {
185    pub const CHARACTER: u16 = 1;
186    pub const LIGATURE: u16 = 2;
187    pub const GHOST: u16 = 4;
188    pub const LEFT: u16 = 8;
189    pub const RIGHT: u16 = 16;
190}
191
192/// Whatsit subtypes.
193#[allow(missing_docs)]
194pub mod whatsit_sub {
195    pub const OPEN: u16 = 0;
196    pub const WRITE: u16 = 1;
197    pub const CLOSE: u16 = 2;
198    pub const SPECIAL: u16 = 3;
199    pub const LATE_SPECIAL: u16 = 4;
200    pub const SAVE_POS: u16 = 7;
201    pub const LATE_LUA: u16 = 8;
202    pub const USER_DEFINED: u16 = 9;
203    pub const PDF_LITERAL: u16 = 16;
204    pub const PDF_LATE_LITERAL: u16 = 17;
205    pub const PDF_REFOBJ: u16 = 18;
206    pub const PDF_ANNOT: u16 = 19;
207    pub const PDF_START_LINK: u16 = 20;
208    pub const PDF_END_LINK: u16 = 21;
209    pub const PDF_DEST: u16 = 22;
210    pub const PDF_ACTION: u16 = 23;
211    pub const PDF_THREAD: u16 = 24;
212    pub const PDF_START_THREAD: u16 = 25;
213    pub const PDF_END_THREAD: u16 = 26;
214    pub const PDF_COLORSTACK: u16 = 29;
215    pub const PDF_SETMATRIX: u16 = 30;
216    pub const PDF_SAVE: u16 = 31;
217    pub const PDF_RESTORE: u16 = 32;
218    pub const PDF_LINK_STATE: u16 = 33;
219}
220
221/// A list of attribute (number, value) pairs, sorted by number.
222pub type AttrList = Rc<[(u32, i32)]>;
223
224/// Box fields shared by hlist and vlist nodes.
225#[derive(Clone, Debug, Default, PartialEq)]
226pub struct BoxData {
227    /// Width.
228    pub width: i32,
229    /// Depth.
230    pub depth: i32,
231    /// Height.
232    pub height: i32,
233    /// Direction (0 = TLT).
234    pub dir: i8,
235    /// Shift amount.
236    pub shift: i32,
237    /// Glue order of the setting.
238    pub glue_order: u8,
239    /// Glue sign: 0 normal, 1 stretching, 2 shrinking.
240    pub glue_sign: u8,
241    /// Glue set ratio. LuaTeX keeps it in single precision, so computed
242    /// ratios are rounded to `f32` (see [`glue_ratio`]).
243    pub glue_set: f64,
244    /// The list.
245    pub head: NodeRef,
246}
247
248/// Fields of a glyph node.
249#[derive(Clone, Debug, Default, PartialEq)]
250pub struct GlyphData {
251    /// The character.
252    pub chr: u32,
253    /// The font.
254    pub font: u32,
255    /// The language.
256    pub lang: u16,
257    /// `\lefthyphenmin` for this glyph.
258    pub left: u8,
259    /// `\righthyphenmin` for this glyph.
260    pub right: u8,
261    /// `\uchyph`.
262    pub uchyph: bool,
263    /// Ligature components.
264    pub components: NodeRef,
265    /// Horizontal offset.
266    pub xoffset: i32,
267    /// Vertical offset.
268    pub yoffset: i32,
269    /// Font expansion factor.
270    pub expansion_factor: i32,
271    /// User data.
272    pub data: i32,
273}
274
275/// Type-specific node data.
276#[derive(Clone, Debug, PartialEq)]
277pub enum NodeData {
278    /// A list head or other temporary node.
279    Temp,
280    /// An hlist or vlist (`id` tells which).
281    Box(Box<BoxData>),
282    /// A rule.
283    Rule {
284        /// Width (or "running").
285        width: i32,
286        /// Depth.
287        depth: i32,
288        /// Height.
289        height: i32,
290        /// Direction.
291        dir: i8,
292        /// Resource index (box/image rules).
293        index: i32,
294        /// Left offset.
295        left: i32,
296        /// Right offset.
297        right: i32,
298    },
299    /// An insertion.
300    Ins {
301        /// `\floatingpenalty`.
302        cost: i32,
303        /// Depth.
304        depth: i32,
305        /// Natural height plus depth.
306        height: i32,
307        /// `\splittopskip`.
308        spec: Glue,
309        /// The vertical list.
310        head: NodeRef,
311    },
312    /// A mark.
313    Mark {
314        /// The marks class.
315        class: u32,
316        /// The token list.
317        mark: TokenList,
318    },
319    /// A `\vadjust`.
320    Adjust {
321        /// The vertical list.
322        head: NodeRef,
323    },
324    /// A boundary.
325    Boundary {
326        /// The value.
327        value: i32,
328    },
329    /// A discretionary.
330    Disc {
331        /// Pre-break list.
332        pre: NodeRef,
333        /// Post-break list.
334        post: NodeRef,
335        /// Replacement list.
336        replace: NodeRef,
337        /// Penalty.
338        penalty: i32,
339    },
340    /// A whatsit.
341    Whatsit(Box<Whatsit>),
342    /// A local paragraph node.
343    LocalPar {
344        /// `\localinterlinepenalty`.
345        pen_inter: i32,
346        /// `\localbrokenpenalty`.
347        pen_broken: i32,
348        /// Paragraph direction.
349        dir: i8,
350        /// `\localleftbox`.
351        box_left: NodeRef,
352        /// Its width.
353        box_left_width: i32,
354        /// `\localrightbox`.
355        box_right: NodeRef,
356        /// Its width.
357        box_right_width: i32,
358    },
359    /// A direction change.
360    Dir {
361        /// The direction.
362        dir: i8,
363        /// The group level.
364        level: i32,
365    },
366    /// Math on/off.
367    Math {
368        /// `\mathsurround`.
369        surround: i32,
370        /// `\mathsurroundskip` as glue.
371        glue: Glue,
372    },
373    /// Glue.
374    Glue {
375        /// The specification.
376        spec: Glue,
377        /// Leader box or rule.
378        leader: NodeRef,
379    },
380    /// A kern.
381    Kern {
382        /// The amount.
383        kern: i32,
384        /// Font expansion factor.
385        expansion_factor: i32,
386    },
387    /// A penalty.
388    Penalty {
389        /// The penalty.
390        penalty: i32,
391    },
392    /// An unset box in an alignment.
393    Unset {
394        /// Box fields (glue_set unused).
395        b: Box<BoxData>,
396        /// Number of spanned columns minus one.
397        span_count: u16,
398        /// Total stretch.
399        stretch: i32,
400        /// Total shrink.
401        shrink: i32,
402        /// Stretch order.
403        stretch_order: u8,
404        /// Shrink order.
405        shrink_order: u8,
406    },
407    /// A glyph (stored inline: glyphs are the most frequent nodes).
408    Glyph(GlyphData),
409    /// A margin kern.
410    MarginKern {
411        /// Width.
412        width: i32,
413        /// The glyph it belongs to.
414        glyph: NodeRef,
415    },
416    /// A noad (`noad`, `radical` and `accent` ids).
417    Noad(Box<NoadData>),
418    /// A generalized fraction noad.
419    Fraction(Box<FractionData>),
420    /// A `\left`, `\middle` or `\right` noad.
421    Fence(Box<FenceData>),
422    /// A style node (`\displaystyle` …); the style is 0–7 as in TeX.
423    Style {
424        /// The style.
425        style: u8,
426    },
427    /// A `\mathchoice` node: display, text, script and scriptscript lists.
428    Choice(Box<[NodeRef; 4]>),
429    /// A `math_char` or `math_text_char` kernel.
430    MathChar {
431        /// The family.
432        fam: u32,
433        /// The character.
434        chr: u32,
435    },
436    /// A `sub_box` or `sub_mlist` kernel.
437    SubList {
438        /// The box (sub_box) or the mlist (sub_mlist).
439        head: NodeRef,
440    },
441    /// A delimiter specification (`delim` node).
442    Delim {
443        /// Family of the small variant.
444        small_fam: u32,
445        /// Character of the small variant.
446        small_char: u32,
447        /// Family of the large variant.
448        large_fam: u32,
449        /// Character of the large variant.
450        large_char: u32,
451    },
452    /// Nodes not modelled in detail.
453    Other(Vec<i64>),
454}
455
456/// Fields of a noad (LuaTeX's `noad`, `radical` and `accent` nodes). The
457/// nucleus and the scripts are kernel nodes (`math_char`,
458/// `math_text_char`, `sub_box`, `sub_mlist`), or null when empty.
459#[derive(Clone, Debug, Default, PartialEq)]
460pub struct NoadData {
461    /// The nucleus.
462    pub nucleus: NodeRef,
463    /// The subscript.
464    pub sub: NodeRef,
465    /// The superscript.
466    pub sup: NodeRef,
467    /// Rendering options (LuaTeX's option bits).
468    pub options: u32,
469    /// Radicals: the delimiter (a `delim` node).
470    pub left: NodeRef,
471    /// Radicals: the degree of `\Uroot`.
472    pub degree: NodeRef,
473    /// Radicals: the requested width (`\Uhextensible`, `width` keyword).
474    pub width: i32,
475    /// Accents: the top accent (a `math_char` kernel).
476    pub accent: NodeRef,
477    /// Accents: the bottom accent.
478    pub bot_accent: NodeRef,
479    /// Accents: the `fraction` criterion (0 when not given).
480    pub fraction: i32,
481    /// The translation into an hlist (`new_hlist`), during
482    /// `mlist_to_hlist`.
483    pub new_hlist: NodeRef,
484}
485
486/// Fields of a generalized fraction noad.
487#[derive(Clone, Debug, PartialEq)]
488pub struct FractionData {
489    /// Rule thickness, or [`DEFAULT_CODE`].
490    pub thickness: i32,
491    /// The numerator (a `sub_mlist` kernel).
492    pub num: NodeRef,
493    /// The denominator (a `sub_mlist` kernel).
494    pub denom: NodeRef,
495    /// Left delimiter (`delim` node) or null.
496    pub left: NodeRef,
497    /// Right delimiter or null.
498    pub right: NodeRef,
499    /// Middle delimiter (`\Uskewed`) or null.
500    pub middle: NodeRef,
501    /// Rendering options.
502    pub options: u32,
503    /// The translation (`new_hlist`).
504    pub new_hlist: NodeRef,
505}
506
507/// Fields of a fence noad (`\left`, `\middle`, `\right`).
508#[derive(Clone, Debug, Default, PartialEq)]
509pub struct FenceData {
510    /// The delimiter (a `delim` node).
511    pub delim: NodeRef,
512    /// Italic correction.
513    pub italic: i32,
514    /// Requested height (0 when not given).
515    pub height: i32,
516    /// Requested depth.
517    pub depth: i32,
518    /// Rendering options.
519    pub options: u32,
520    /// Spacing class (−1 when not given).
521    pub class: i32,
522    /// The translation (`new_hlist`).
523    pub new_hlist: NodeRef,
524}
525
526/// The fraction thickness meaning "the default rule thickness"
527/// (`default_code`).
528pub const DEFAULT_CODE: i32 = 0o10000000000;
529
530/// Noad subtypes (LuaTeX numbering).
531#[allow(missing_docs)]
532pub mod noad_sub {
533    pub const ORD: u16 = 0;
534    pub const OP_DISPLAY_LIMITS: u16 = 1;
535    pub const OP_LIMITS: u16 = 2;
536    pub const OP_NO_LIMITS: u16 = 3;
537    pub const BIN: u16 = 4;
538    pub const REL: u16 = 5;
539    pub const OPEN: u16 = 6;
540    pub const CLOSE: u16 = 7;
541    pub const PUNCT: u16 = 8;
542    pub const INNER: u16 = 9;
543    pub const UNDER: u16 = 10;
544    pub const OVER: u16 = 11;
545    pub const VCENTER: u16 = 12;
546}
547
548/// Radical subtypes.
549#[allow(missing_docs)]
550pub mod radical_sub {
551    pub const RADICAL: u16 = 0;
552    pub const URADICAL: u16 = 1;
553    pub const UROOT: u16 = 2;
554    pub const UUNDERDELIMITER: u16 = 3;
555    pub const UOVERDELIMITER: u16 = 4;
556    pub const UDELIMITERUNDER: u16 = 5;
557    pub const UDELIMITEROVER: u16 = 6;
558    pub const UHEXTENSIBLE: u16 = 7;
559}
560
561/// Fence subtypes.
562#[allow(missing_docs)]
563pub mod fence_sub {
564    pub const UNSET: u16 = 0;
565    pub const LEFT: u16 = 1;
566    pub const MIDDLE: u16 = 2;
567    pub const RIGHT: u16 = 3;
568    pub const NO: u16 = 4;
569}
570
571/// Accent subtypes.
572#[allow(missing_docs)]
573pub mod accent_sub {
574    pub const BOTH_FLEXIBLE: u16 = 0;
575    pub const FIXED_TOP: u16 = 1;
576    pub const FIXED_BOTTOM: u16 = 2;
577    pub const FIXED_BOTH: u16 = 3;
578}
579
580/// Noad option bits (LuaTeX manual, "Math core nodes").
581#[allow(missing_docs)]
582pub mod noad_opt {
583    pub const SET: u32 = 0x08;
584    pub const AXIS: u32 = 0x02 | SET;
585    pub const NO_AXIS: u32 = 0x04 | SET;
586    pub const EXACT: u32 = 0x10 | SET;
587    pub const LEFT: u32 = 0x11 | SET;
588    pub const MIDDLE: u32 = 0x12 | SET;
589    pub const RIGHT: u32 = 0x14 | SET;
590    pub const NO_SUB_SCRIPT: u32 = 0x21 | SET;
591    pub const NO_SUPER_SCRIPT: u32 = 0x22 | SET;
592    /// retex: an overlay accent (`\Umathaccent overlay`).
593    pub const OVERLAY: u32 = 0x100;
594}
595
596/// Whatsit payloads.
597#[derive(Clone, Debug, PartialEq)]
598pub enum Whatsit {
599    /// `\openout`.
600    Open {
601        /// Stream number.
602        stream: u32,
603        /// File name.
604        name: Vec<u8>,
605    },
606    /// `\write`.
607    Write {
608        /// Stream number.
609        stream: i32,
610        /// Unexpanded token list.
611        data: TokenList,
612    },
613    /// `\closeout`.
614    Close {
615        /// Stream number.
616        stream: u32,
617    },
618    /// `\special`.
619    Special {
620        /// The text.
621        data: Vec<u8>,
622    },
623    /// `\latelua`.
624    LateLua {
625        /// Token list or string.
626        data: LateData,
627        /// Chunk name.
628        name: Vec<u8>,
629    },
630    /// `\pdfextension literal` and friends.
631    PdfLiteral {
632        /// Mode (0 origin, 1 page, 2 direct, …).
633        mode: u8,
634        /// Data.
635        data: LateData,
636        /// Whether this is a late literal.
637        late: bool,
638    },
639    /// `\savepos`.
640    SavePos,
641    /// `\pdfextension colorstack`.
642    PdfColorstack {
643        /// Stack number.
644        stack: i32,
645        /// Command (set, push, pop, current).
646        cmd: u8,
647        /// Data.
648        data: Vec<u8>,
649    },
650    /// `\pdfextension setmatrix`.
651    PdfSetmatrix {
652        /// The matrix text.
653        data: Vec<u8>,
654    },
655    /// `\pdfextension save`.
656    PdfSave,
657    /// `\pdfextension restore`.
658    PdfRestore,
659    /// `\pdfextension annot`.
660    PdfAnnot {
661        /// `width`, `height`, `depth` ([`RUNNING`] when not given).
662        dims: [i32; 3],
663        /// The annotation's object number.
664        objnum: i32,
665        /// The annotation's text.
666        data: Vec<u8>,
667    },
668    /// `\pdfextension startlink`.
669    PdfStartLink {
670        /// `width`, `height`, `depth` ([`RUNNING`] when not given).
671        dims: [i32; 3],
672        /// The object number of the link's first annotation.
673        objnum: i32,
674        /// `attr {…}`.
675        attr: Vec<u8>,
676        /// The action.
677        action: crate::pdfext::PdfAction,
678    },
679    /// `\pdfextension endlink`.
680    PdfEndLink,
681    /// `\pdfextension dest`.
682    PdfDest {
683        /// The destination's identifier.
684        id: crate::pdfext::DestId,
685        /// The view.
686        view: crate::pdfext::DestView,
687        /// `struct n`: a structure destination for structure element
688        /// object `n`.
689        structure: Option<i32>,
690    },
691    /// `\pdfextension refobj`.
692    PdfRefobj {
693        /// The object number.
694        objnum: i32,
695    },
696    /// `\pdfextension linkstate`.
697    PdfLinkState {
698        /// The state (0 links on, 1 links off).
699        state: i32,
700    },
701    /// A `user_defined` whatsit (made from Lua).
702    UserDefined {
703        /// `user_id`.
704        user_id: i32,
705        /// The value, by type.
706        value: UserValue,
707    },
708    /// Other whatsits, kept opaque for now.
709    Other {
710        /// Raw fields.
711        fields: Vec<i64>,
712        /// Raw data.
713        data: Vec<u8>,
714    },
715}
716
717/// Data of late whatsits: an unexpanded token list or a string.
718#[derive(Clone, Debug, PartialEq)]
719pub enum LateData {
720    /// Tokens, expanded at shipout.
721    Tokens(TokenList),
722    /// An already expanded string.
723    Bytes(Vec<u8>),
724    /// A string set from Lua outside IniTeX, which LuaTeX keeps as a Lua
725    /// reference (observed: shown as `<lua data reference N>`).
726    Lua {
727        /// The string.
728        data: Vec<u8>,
729        /// The reference number shown.
730        reference: i32,
731    },
732}
733
734impl LateData {
735    /// The string of a [`LateData::Bytes`] or [`LateData::Lua`] value.
736    pub fn bytes(&self) -> Option<&[u8]> {
737        match self {
738            LateData::Bytes(b) | LateData::Lua { data: b, .. } => Some(b),
739            LateData::Tokens(_) => None,
740        }
741    }
742}
743
744/// The value of a `user_defined` whatsit, by its type (LuaTeX manual:
745/// `a`, `d`, `l`, `n`, `s`, `t`).
746#[derive(Clone, Debug, PartialEq)]
747pub enum UserValue {
748    /// `d`: a number.
749    Int(i32),
750    /// `s`: a string.
751    Str(Vec<u8>),
752    /// `t`: a token list.
753    Tokens(TokenList),
754    /// `n`: a node list.
755    Nodes(NodeRef),
756    /// `l`: a Lua value, by its reference number.
757    Lua(i32),
758    /// `a`: an attribute list (not modelled).
759    Attr,
760}
761
762/// A node.
763#[derive(Clone, Debug, PartialEq)]
764pub struct Node {
765    /// The type id.
766    pub id: u8,
767    /// The subtype.
768    pub subtype: u16,
769    /// Next node in the list.
770    pub next: NodeRef,
771    /// Previous node in the list.
772    pub prev: NodeRef,
773    /// Attributes.
774    pub attr: Option<AttrList>,
775    /// Type-specific data.
776    pub data: NodeData,
777}
778
779impl Node {
780    /// A temporary node, like the null node.
781    fn temp() -> Node {
782        Node {
783            id: id::TEMP,
784            subtype: 0,
785            next: NodeRef::NULL,
786            prev: NodeRef::NULL,
787            attr: None,
788            data: NodeData::Temp,
789        }
790    }
791}
792
793/// What happened to a node with a Lua property (`node.setproperty`),
794/// for the Lua side to update its property table.
795#[derive(Debug, Clone, Copy, PartialEq, Eq)]
796pub enum PropEvent {
797    /// The node was freed: its property goes.
798    Flush(u32),
799    /// The node (first) was copied (second).
800    Copy(u32, u32),
801}
802
803/// LuaTeX's node properties as the engine tracks them: which nodes have
804/// one, the mode set by `node.set_properties_mode(flush, copy)`, and the
805/// frees and copies of such nodes since the Lua side last looked.
806#[derive(Debug, Clone, Default)]
807pub struct Properties {
808    /// Nodes with a property (only tracked when `flush` or `copy` is on).
809    pub nodes: std::collections::HashSet<u32>,
810    /// Properties go when their node is freed.
811    pub flush: bool,
812    /// Copies of a node get a property that inherits from the original.
813    pub copy: bool,
814    /// Pending events, oldest first.
815    pub events: Vec<PropEvent>,
816}
817
818/// The node arena.
819#[derive(Debug, Clone)]
820pub struct Nodes {
821    nodes: Vec<Node>,
822    /// The nesting depth of [`Nodes::copy_list`].
823    copy_depth: u32,
824    /// Where writes to nodes outside the arena go: Lua code can name any
825    /// number as a direct node.
826    sink: Node,
827    free: Vec<u32>,
828    /// Number of nodes in use.
829    pub in_use: usize,
830    /// Lua node properties.
831    pub props: Properties,
832    /// SyncTeX fields of the nodes, while SyncTeX is on (see
833    /// [`SyncFields`]).
834    pub sync: Option<Box<SyncFields>>,
835    /// The first node freed twice, with its type before the first free:
836    /// a fatal error in LuaTeX, reported by the engine (see
837    /// `Engine::check_double_free`).
838    pub double_free: Option<(u8, u32)>,
839    /// The temporary head of a paragraph during its node processing
840    /// callbacks: LuaTeX's is not a node that can be freed.
841    pub par_temp: NodeRef,
842    /// The numbers of the Lua values nodes keep by reference.
843    pub lua_refs: LuaRefs,
844}
845
846/// The numbers LuaTeX gives the Lua values it keeps by reference (shown
847/// as `<lua data reference N>`), modelled as observed: a new reference
848/// takes the most recently released number, else the next one; freeing
849/// or overwriting a node's value releases its number, copying the node
850/// takes a new one. Fonts made from Lua tables and luaotfload's set-up
851/// hold references too.
852#[derive(Debug, Clone, Default)]
853pub struct LuaRefs {
854    /// The next number never used (780 at the start of a plain run).
855    pub next: i32,
856    /// The released numbers, the last released last.
857    pub free: Vec<i32>,
858    /// References made by copying nodes: (copy, original), for the Lua
859    /// side to duplicate the value.
860    pub copies: Vec<(i32, i32)>,
861}
862
863impl LuaRefs {
864    /// A new reference number.
865    pub fn take(&mut self) -> i32 {
866        if let Some(r) = self.free.pop() {
867            return r;
868        }
869        if self.next == 0 {
870            self.next = 780;
871        }
872        let r = self.next;
873        self.next += 1;
874        r
875    }
876
877    /// Releases reference `r` (none if it is not positive).
878    pub fn release(&mut self, r: i32) {
879        if r > 0 {
880            self.free.push(r);
881        }
882    }
883
884    /// The references held by a font that luaotfload defines: one while
885    /// it is made, released after, and one kept (observed).
886    pub fn font_defined(&mut self, temporary: bool) {
887        if temporary {
888            let t = self.take();
889            self.take();
890            self.release(t);
891        } else {
892            self.take();
893        }
894    }
895}
896
897/// The reference number of the Lua value node data keeps, if any.
898fn lua_ref_mut(data: &mut NodeData) -> Option<&mut i32> {
899    match data {
900        NodeData::Whatsit(w) => match &mut **w {
901            Whatsit::LateLua {
902                data: LateData::Lua { reference, .. },
903                ..
904            }
905            | Whatsit::PdfLiteral {
906                data: LateData::Lua { reference, .. },
907                ..
908            } => Some(reference),
909            Whatsit::UserDefined {
910                value: UserValue::Lua(r),
911                ..
912            } if *r > 0 => Some(r),
913            _ => None,
914        },
915        _ => None,
916    }
917}
918
919/// The SyncTeX fields (input tag and line) of the nodes, kept beside the
920/// arena while SyncTeX is on, so that runs without it pay nothing.
921///
922/// As LuaTeX's `node.direct.get_synctex_fields` shows, a node gets the
923/// current tag and line when it is made, except glyphs and kerns, which get
924/// none; a copy keeps the fields of its original, except kerns and math
925/// nodes, which get the current ones (observed).
926#[derive(Debug, Clone, Default)]
927pub struct SyncFields {
928    /// The tag and line of the input being read.
929    pub cur: (u32, i32),
930    fields: Vec<(u32, i32)>,
931}
932
933impl SyncFields {
934    #[inline]
935    fn set(&mut self, i: u32, f: (u32, i32)) {
936        let i = i as usize;
937        if i >= self.fields.len() {
938            self.fields.resize(i + 1, (0, 0));
939        }
940        self.fields[i] = f;
941    }
942}
943
944impl Default for Nodes {
945    fn default() -> Self {
946        Self::new()
947    }
948}
949
950impl Nodes {
951    /// An arena with only the null node.
952    pub fn new() -> Self {
953        Nodes {
954            nodes: vec![Node::temp()],
955            copy_depth: 0,
956            sink: Node::temp(),
957            free: Vec::new(),
958            in_use: 0,
959            props: Properties::default(),
960            sync: None,
961            lua_refs: LuaRefs::default(),
962            double_free: None,
963            par_temp: NodeRef::NULL,
964        }
965    }
966
967    /// Starts keeping SyncTeX fields (for nodes made from now on).
968    pub fn enable_sync(&mut self) {
969        if self.sync.is_none() {
970            self.sync = Some(Box::default());
971        }
972    }
973
974    /// The SyncTeX tag and line of `p`, (0, 0) if it has none.
975    #[inline]
976    pub fn sync_fields(&self, p: NodeRef) -> (u32, i32) {
977        self.sync
978            .as_ref()
979            .and_then(|s| s.fields.get(p.0 as usize).copied())
980            .unwrap_or((0, 0))
981    }
982
983    /// Sets the SyncTeX tag and line of `p` (when SyncTeX is on).
984    pub fn set_sync_fields(&mut self, p: NodeRef, f: (u32, i32)) {
985        if p.0 as usize >= self.nodes.len() {
986            return;
987        }
988        if let Some(s) = self.sync.as_deref_mut() {
989            s.set(p.0, f);
990        }
991    }
992
993    /// Sets the SyncTeX fields of `p`, a node just made by font
994    /// processing that LuaTeX's Lua code makes by copying (kerns from
995    /// OpenType positioning): the current tag and line.
996    pub fn sync_as_copied(&mut self, p: NodeRef) {
997        if let Some(s) = self.sync.as_deref_mut() {
998            let cur = s.cur;
999            s.set(p.0, cur);
1000        }
1001    }
1002
1003    /// The node at `p`; the null node if `p` is outside the arena.
1004    #[inline]
1005    fn at(&self, p: NodeRef) -> &Node {
1006        self.nodes.get(p.0 as usize).unwrap_or(&self.nodes[0])
1007    }
1008
1009    /// The node at `p`, mutably; a scratch node if `p` is outside the
1010    /// arena.
1011    #[inline]
1012    fn at_mut(&mut self, p: NodeRef) -> &mut Node {
1013        match self.nodes.get_mut(p.0 as usize) {
1014            Some(n) => n,
1015            None => {
1016                self.sink = Node::temp();
1017                &mut self.sink
1018            }
1019        }
1020    }
1021
1022    /// The number of node slots in the arena.
1023    pub fn capacity(&self) -> usize {
1024        self.nodes.len()
1025    }
1026
1027    /// Allocates a node.
1028    pub fn alloc(&mut self, id: u8, subtype: u16, data: NodeData) -> NodeRef {
1029        let node = Node {
1030            id,
1031            subtype,
1032            next: NodeRef::NULL,
1033            prev: NodeRef::NULL,
1034            attr: None,
1035            data,
1036        };
1037        self.in_use += 1;
1038        let p = if let Some(i) = self.free.pop() {
1039            self.nodes[i as usize] = node;
1040            NodeRef(i)
1041        } else {
1042            self.nodes.push(node);
1043            NodeRef(self.nodes.len() as u32 - 1)
1044        };
1045        if let Some(s) = self.sync.as_deref_mut() {
1046            let f = if id == id::KERN || id == id::GLYPH {
1047                (0, 0)
1048            } else {
1049                s.cur
1050            };
1051            s.set(p.0, f);
1052        }
1053        p
1054    }
1055
1056    /// Frees a single node (not its sublists).
1057    pub fn free(&mut self, p: NodeRef) {
1058        // Lua code can free a node twice (LuaTeX corrupts its memory);
1059        // a node in the free list twice would be allocated twice.
1060        if p.is_null() || p.0 as usize >= self.nodes.len() {
1061            return;
1062        }
1063        if self.is_free(p) {
1064            if self.double_free.is_none() {
1065                self.double_free = Some((self.at(p).subtype as u8, p.0));
1066            }
1067            return;
1068        }
1069        // The subtype of a free node keeps its type, for the message.
1070        let t = self.at(p).id;
1071        self.at_mut(p).subtype = u16::from(t);
1072        if let Some(r) = lua_ref_mut(&mut self.at_mut(p).data) {
1073            let r = *r;
1074            self.lua_refs.release(r);
1075        }
1076        self.at_mut(p).id = id::FREE;
1077        self.at_mut(p).data = NodeData::Temp;
1078        self.at_mut(p).attr = None;
1079        self.free.push(p.0);
1080        self.in_use -= 1;
1081        if !self.props.nodes.is_empty() && self.props.nodes.remove(&p.0) && self.props.flush {
1082            self.props.events.push(PropEvent::Flush(p.0));
1083        }
1084    }
1085
1086    /// The reference number of the Lua value node `p` keeps, if any.
1087    pub fn lua_ref_of(&mut self, p: NodeRef) -> Option<i32> {
1088        lua_ref_mut(&mut self.at_mut(p).data).map(|r| *r)
1089    }
1090
1091    /// Whether `p` was freed (and not allocated again).
1092    #[inline]
1093    pub fn is_free(&self, p: NodeRef) -> bool {
1094        self.at(p).id == id::FREE
1095    }
1096
1097    /// The node at `p`.
1098    #[inline]
1099    pub fn get(&self, p: NodeRef) -> &Node {
1100        self.at(p)
1101    }
1102
1103    /// The node at `p`, mutably.
1104    #[inline]
1105    pub fn get_mut(&mut self, p: NodeRef) -> &mut Node {
1106        self.at_mut(p)
1107    }
1108
1109    /// The type id of `p`.
1110    #[inline]
1111    pub fn id(&self, p: NodeRef) -> u8 {
1112        self.at(p).id
1113    }
1114
1115    /// The `next` link of `p`.
1116    #[inline]
1117    pub fn next(&self, p: NodeRef) -> NodeRef {
1118        self.at(p).next
1119    }
1120
1121    /// Links `q` after `p` (sets `p.next` and `q.prev`).
1122    #[inline]
1123    pub fn link(&mut self, p: NodeRef, q: NodeRef) {
1124        self.at_mut(p).next = q;
1125        if !q.is_null() {
1126            self.at_mut(q).prev = p;
1127        }
1128    }
1129
1130    /// The box data of an hlist/vlist node.
1131    pub fn box_data(&self, p: NodeRef) -> Option<&BoxData> {
1132        match &self.at(p).data {
1133            NodeData::Box(b) => Some(b),
1134            NodeData::Unset { b, .. } => Some(b),
1135            _ => None,
1136        }
1137    }
1138
1139    /// The box data of an hlist/vlist node, mutably.
1140    pub fn box_data_mut(&mut self, p: NodeRef) -> Option<&mut BoxData> {
1141        match &mut self.at_mut(p).data {
1142            NodeData::Box(b) => Some(b),
1143            NodeData::Unset { b, .. } => Some(b),
1144            _ => None,
1145        }
1146    }
1147
1148    /// The last node of the list starting at `p`.
1149    pub fn tail_of(&self, mut p: NodeRef) -> NodeRef {
1150        if p.is_null() {
1151            return p;
1152        }
1153        loop {
1154            let n = self.next(p);
1155            if n.is_null() {
1156                return p;
1157            }
1158            p = n;
1159        }
1160    }
1161
1162    /// The last node of the list starting at `p`, or `None` if the list
1163    /// is circular (Lua code can link nodes into a loop).
1164    pub fn bounded_tail(&self, mut p: NodeRef) -> Option<NodeRef> {
1165        if p.is_null() {
1166            return Some(p);
1167        }
1168        for _ in 0..self.nodes.len() {
1169            let n = self.next(p);
1170            if n.is_null() {
1171                return Some(p);
1172            }
1173            p = n;
1174        }
1175        None
1176    }
1177
1178    /// Frees a whole node list, including sublists (`flush_node_list`).
1179    pub fn flush_list(&mut self, p: NodeRef) {
1180        let mut stack: Vec<NodeRef> = Vec::new();
1181        let mut p = p;
1182        loop {
1183            while !p.is_null() {
1184                if self.is_free(p) {
1185                    // Freed already: a list that Lua code made circular,
1186                    // or a node in two lists.
1187                    self.free(p);
1188                    break;
1189                }
1190                let next = self.next(p);
1191                match &self.at(p).data {
1192                    NodeData::Box(b) => stack.push(b.head),
1193                    NodeData::Unset { b, .. } => stack.push(b.head),
1194                    NodeData::Ins { head, .. } | NodeData::Adjust { head } => stack.push(*head),
1195                    NodeData::Disc {
1196                        pre, post, replace, ..
1197                    } => {
1198                        stack.push(*pre);
1199                        stack.push(*post);
1200                        stack.push(*replace);
1201                    }
1202                    NodeData::Glue { leader, .. } => stack.push(*leader),
1203                    NodeData::Glyph(g) => {
1204                        if !g.components.is_null() {
1205                            stack.push(g.components);
1206                        }
1207                    }
1208                    NodeData::LocalPar {
1209                        box_left,
1210                        box_right,
1211                        ..
1212                    } => {
1213                        stack.push(*box_left);
1214                        stack.push(*box_right);
1215                    }
1216                    NodeData::Noad(n) => stack.extend([
1217                        n.nucleus,
1218                        n.sub,
1219                        n.sup,
1220                        n.left,
1221                        n.degree,
1222                        n.accent,
1223                        n.bot_accent,
1224                        n.new_hlist,
1225                    ]),
1226                    NodeData::Fraction(f) => {
1227                        stack.extend([f.num, f.denom, f.left, f.right, f.middle, f.new_hlist])
1228                    }
1229                    NodeData::Fence(f) => stack.extend([f.delim, f.new_hlist]),
1230                    NodeData::Choice(c) => stack.extend(c.iter().copied()),
1231                    NodeData::SubList { head } => stack.push(*head),
1232                    _ => {}
1233                }
1234                self.free(p);
1235                p = next;
1236            }
1237            match stack.pop() {
1238                Some(q) => p = q,
1239                None => return,
1240            }
1241        }
1242    }
1243
1244    /// Copies a whole node list, including sublists (`copy_node_list`).
1245    pub fn copy_list(&mut self, mut p: NodeRef) -> NodeRef {
1246        let mut head = NodeRef::NULL;
1247        let mut tail = NodeRef::NULL;
1248        // Lists nested deeper than this are cut off: only Lua code makes
1249        // them, with a box that contains itself (LuaTeX crashes).
1250        if self.copy_depth >= MAX_NESTING {
1251            return head;
1252        }
1253        self.copy_depth += 1;
1254        // A list longer than the arena is circular (made by Lua code;
1255        // LuaTeX copies until memory runs out): the copy stops.
1256        let mut left = self.nodes.len();
1257        while !p.is_null() && left > 0 {
1258            left -= 1;
1259            let q = self.copy_node(p);
1260            if head.is_null() {
1261                head = q;
1262            } else {
1263                self.link(tail, q);
1264            }
1265            tail = q;
1266            p = self.next(p);
1267        }
1268        self.copy_depth -= 1;
1269        head
1270    }
1271
1272    /// Copies one node with its sublists; the copy's links are null.
1273    pub fn copy_node(&mut self, p: NodeRef) -> NodeRef {
1274        let mut node = self.at(p).clone();
1275        node.next = NodeRef::NULL;
1276        node.prev = NodeRef::NULL;
1277        match &mut node.data {
1278            NodeData::Box(b) => b.head = self.copy_list(b.head),
1279            NodeData::Unset { b, .. } => b.head = self.copy_list(b.head),
1280            NodeData::Ins { head, .. } | NodeData::Adjust { head } => *head = self.copy_list(*head),
1281            NodeData::Disc {
1282                pre, post, replace, ..
1283            } => {
1284                *pre = self.copy_list(*pre);
1285                *post = self.copy_list(*post);
1286                *replace = self.copy_list(*replace);
1287            }
1288            NodeData::Glue { leader, .. } => *leader = self.copy_list(*leader),
1289            NodeData::Glyph(g) => g.components = self.copy_list(g.components),
1290            NodeData::LocalPar {
1291                box_left,
1292                box_right,
1293                ..
1294            } => {
1295                *box_left = self.copy_list(*box_left);
1296                *box_right = self.copy_list(*box_right);
1297            }
1298            NodeData::Noad(n) => {
1299                for f in [
1300                    &mut n.nucleus,
1301                    &mut n.sub,
1302                    &mut n.sup,
1303                    &mut n.left,
1304                    &mut n.degree,
1305                    &mut n.accent,
1306                    &mut n.bot_accent,
1307                    &mut n.new_hlist,
1308                ] {
1309                    *f = self.copy_list(*f);
1310                }
1311            }
1312            NodeData::Fraction(fr) => {
1313                for f in [
1314                    &mut fr.num,
1315                    &mut fr.denom,
1316                    &mut fr.left,
1317                    &mut fr.right,
1318                    &mut fr.middle,
1319                    &mut fr.new_hlist,
1320                ] {
1321                    *f = self.copy_list(*f);
1322                }
1323            }
1324            NodeData::Fence(fe) => {
1325                fe.delim = self.copy_list(fe.delim);
1326                fe.new_hlist = self.copy_list(fe.new_hlist);
1327            }
1328            NodeData::Choice(c) => {
1329                for f in c.iter_mut() {
1330                    *f = self.copy_list(*f);
1331                }
1332            }
1333            NodeData::SubList { head } => *head = self.copy_list(*head),
1334            _ => {}
1335        }
1336        if let Some(r) = lua_ref_mut(&mut node.data) {
1337            let old = *r;
1338            let new = self.lua_refs.take();
1339            *r = new;
1340            self.lua_refs.copies.push((new, old));
1341        }
1342        self.in_use += 1;
1343        let q = if let Some(i) = self.free.pop() {
1344            self.nodes[i as usize] = node;
1345            NodeRef(i)
1346        } else {
1347            self.nodes.push(node);
1348            NodeRef(self.nodes.len() as u32 - 1)
1349        };
1350        if self.props.copy && self.props.nodes.contains(&p.0) {
1351            self.props.nodes.insert(q.0);
1352            self.props.events.push(PropEvent::Copy(p.0, q.0));
1353        }
1354        if self.sync.is_some() {
1355            let id = self.at(q).id;
1356            let f = if id == id::KERN || id == id::MATH {
1357                self.sync.as_ref().map_or((0, 0), |s| s.cur)
1358            } else {
1359                self.sync_fields(p)
1360            };
1361            self.set_sync_fields(q, f);
1362        }
1363        q
1364    }
1365
1366    /// A new empty box of the given type (`new_null_box`).
1367    pub fn new_null_box(&mut self, hlist: bool) -> NodeRef {
1368        self.alloc(
1369            if hlist { id::HLIST } else { id::VLIST },
1370            0,
1371            NodeData::Box(Box::default()),
1372        )
1373    }
1374
1375    /// A new glue node.
1376    pub fn new_glue(&mut self, spec: Glue, subtype: u16) -> NodeRef {
1377        self.alloc(
1378            id::GLUE,
1379            subtype,
1380            NodeData::Glue {
1381                spec,
1382                leader: NodeRef::NULL,
1383            },
1384        )
1385    }
1386
1387    /// A new kern node.
1388    pub fn new_kern(&mut self, w: i32, subtype: u16) -> NodeRef {
1389        self.alloc(
1390            id::KERN,
1391            subtype,
1392            NodeData::Kern {
1393                kern: w,
1394                expansion_factor: 0,
1395            },
1396        )
1397    }
1398
1399    /// A new penalty node.
1400    pub fn new_penalty(&mut self, m: i32, subtype: u16) -> NodeRef {
1401        self.alloc(id::PENALTY, subtype, NodeData::Penalty { penalty: m })
1402    }
1403
1404    /// A new temporary list head.
1405    pub fn new_temp(&mut self) -> NodeRef {
1406        self.alloc(id::TEMP, 0, NodeData::Temp)
1407    }
1408
1409    /// A new glyph node.
1410    pub fn new_glyph(&mut self, font: u32, chr: u32) -> NodeRef {
1411        self.alloc(
1412            id::GLYPH,
1413            0,
1414            NodeData::Glyph(GlyphData {
1415                chr,
1416                font,
1417                ..GlyphData::default()
1418            }),
1419        )
1420    }
1421
1422    /// A new rule node with the given dimensions.
1423    pub fn new_rule(&mut self, width: i32, height: i32, depth: i32) -> NodeRef {
1424        self.alloc(
1425            id::RULE,
1426            0,
1427            NodeData::Rule {
1428                width,
1429                depth,
1430                height,
1431                dir: 0,
1432                index: 0,
1433                left: 0,
1434                right: 0,
1435            },
1436        )
1437    }
1438
1439    /// A new empty discretionary node.
1440    pub fn new_disc(&mut self, subtype: u16, penalty: i32) -> NodeRef {
1441        self.alloc(
1442            id::DISC,
1443            subtype,
1444            NodeData::Disc {
1445                pre: NodeRef::NULL,
1446                post: NodeRef::NULL,
1447                replace: NodeRef::NULL,
1448                penalty,
1449            },
1450        )
1451    }
1452
1453    /// A new math node.
1454    pub fn new_math(&mut self, surround: i32, subtype: u16) -> NodeRef {
1455        self.alloc(
1456            id::MATH,
1457            subtype,
1458            NodeData::Math {
1459                surround,
1460                glue: Glue::ZERO,
1461            },
1462        )
1463    }
1464
1465    /// The type-specific data of `p`.
1466    #[inline]
1467    pub fn data(&self, p: NodeRef) -> &NodeData {
1468        &self.at(p).data
1469    }
1470
1471    /// The type-specific data of `p`, mutably.
1472    #[inline]
1473    pub fn data_mut(&mut self, p: NodeRef) -> &mut NodeData {
1474        &mut self.at_mut(p).data
1475    }
1476
1477    /// The subtype of `p`.
1478    #[inline]
1479    pub fn subtype(&self, p: NodeRef) -> u16 {
1480        self.at(p).subtype
1481    }
1482
1483    /// Sets the subtype of `p`.
1484    #[inline]
1485    pub fn set_subtype(&mut self, p: NodeRef, s: u16) {
1486        self.at_mut(p).subtype = s;
1487    }
1488
1489    /// The `prev` link of `p`.
1490    #[inline]
1491    pub fn prev(&self, p: NodeRef) -> NodeRef {
1492        self.at(p).prev
1493    }
1494
1495    /// Sets only the `next` link of `p`.
1496    #[inline]
1497    pub fn set_next(&mut self, p: NodeRef, q: NodeRef) {
1498        self.at_mut(p).next = q;
1499    }
1500
1501    /// Sets only the `prev` link of `p`.
1502    #[inline]
1503    pub fn set_prev(&mut self, p: NodeRef, q: NodeRef) {
1504        self.at_mut(p).prev = q;
1505    }
1506
1507    /// Whether `p` is a glyph node.
1508    #[inline]
1509    pub fn is_glyph(&self, p: NodeRef) -> bool {
1510        self.at(p).id == id::GLYPH
1511    }
1512
1513    /// The glyph data of a glyph node.
1514    #[inline]
1515    pub fn glyph(&self, p: NodeRef) -> Option<&GlyphData> {
1516        match &self.at(p).data {
1517            NodeData::Glyph(g) => Some(g),
1518            _ => None,
1519        }
1520    }
1521
1522    /// The glyph data of a glyph node, mutably.
1523    #[inline]
1524    pub fn glyph_mut(&mut self, p: NodeRef) -> Option<&mut GlyphData> {
1525        match &mut self.at_mut(p).data {
1526            NodeData::Glyph(g) => Some(g),
1527            _ => None,
1528        }
1529    }
1530
1531    /// The glue specification of a glue node.
1532    #[inline]
1533    pub fn glue_spec(&self, p: NodeRef) -> Glue {
1534        match &self.at(p).data {
1535            NodeData::Glue { spec, .. } => *spec,
1536            NodeData::Math { glue, .. } => *glue,
1537            _ => Glue::ZERO,
1538        }
1539    }
1540
1541    /// The width of a node that has one (boxes, rules, glue, kerns, math,
1542    /// margin kerns, unset nodes); zero otherwise. Glyph widths need the
1543    /// font and are not handled here.
1544    pub fn width(&self, p: NodeRef) -> i32 {
1545        match &self.at(p).data {
1546            NodeData::Box(b) => b.width,
1547            NodeData::Unset { b, .. } => b.width,
1548            NodeData::Rule { width, .. } => *width,
1549            NodeData::Glue { spec, .. } => spec.width,
1550            NodeData::Kern {
1551                kern,
1552                expansion_factor,
1553            } => *kern + *expansion_factor,
1554            NodeData::Math { surround, glue } => {
1555                if glue.width != 0 || glue.stretch != 0 || glue.shrink != 0 {
1556                    glue.width
1557                } else {
1558                    *surround
1559                }
1560            }
1561            NodeData::MarginKern { width, .. } => *width,
1562            _ => 0,
1563        }
1564    }
1565
1566    /// The penalty of a penalty node (0 for other nodes).
1567    pub fn penalty(&self, p: NodeRef) -> i32 {
1568        match &self.at(p).data {
1569            NodeData::Penalty { penalty } => *penalty,
1570            NodeData::Disc { penalty, .. } => *penalty,
1571            _ => 0,
1572        }
1573    }
1574
1575    /// The (pre, post, replace) lists of a discretionary.
1576    pub fn disc_lists(&self, p: NodeRef) -> (NodeRef, NodeRef, NodeRef) {
1577        match &self.at(p).data {
1578            NodeData::Disc {
1579                pre, post, replace, ..
1580            } => (*pre, *post, *replace),
1581            _ => (NodeRef::NULL, NodeRef::NULL, NodeRef::NULL),
1582        }
1583    }
1584
1585    /// Sets the lists of a discretionary.
1586    pub fn set_disc_lists(&mut self, p: NodeRef, l: (NodeRef, NodeRef, NodeRef)) {
1587        if let NodeData::Disc {
1588            pre, post, replace, ..
1589        } = &mut self.at_mut(p).data
1590        {
1591            *pre = l.0;
1592            *post = l.1;
1593            *replace = l.2;
1594        }
1595        for q in [l.0, l.1, l.2] {
1596            if !q.is_null() {
1597                self.at_mut(q).prev = NodeRef::NULL;
1598            }
1599        }
1600    }
1601
1602    /// The list of a box, unset node, insertion or adjust node.
1603    pub fn list_ptr(&self, p: NodeRef) -> NodeRef {
1604        match &self.at(p).data {
1605            NodeData::Box(b) => b.head,
1606            NodeData::Unset { b, .. } => b.head,
1607            NodeData::Ins { head, .. } | NodeData::Adjust { head } => *head,
1608            _ => NodeRef::NULL,
1609        }
1610    }
1611
1612    /// Sets the list of a box, unset node, insertion or adjust node.
1613    pub fn set_list_ptr(&mut self, p: NodeRef, l: NodeRef) {
1614        match &mut self.at_mut(p).data {
1615            NodeData::Box(b) => b.head = l,
1616            NodeData::Unset { b, .. } => b.head = l,
1617            NodeData::Ins { head, .. } | NodeData::Adjust { head } => *head = l,
1618            _ => {}
1619        }
1620    }
1621
1622    /// The attribute list of `p`.
1623    #[inline]
1624    pub fn attr(&self, p: NodeRef) -> Option<&AttrList> {
1625        self.at(p).attr.as_ref()
1626    }
1627
1628    /// Sets the attribute list of `p`.
1629    #[inline]
1630    pub fn set_attr(&mut self, p: NodeRef, a: Option<AttrList>) {
1631        self.at_mut(p).attr = a;
1632    }
1633
1634    /// The noad fields of a noad, radical or accent node.
1635    #[inline]
1636    pub fn noad(&self, p: NodeRef) -> Option<&NoadData> {
1637        match &self.at(p).data {
1638            NodeData::Noad(n) => Some(n),
1639            _ => None,
1640        }
1641    }
1642
1643    /// The noad fields of a noad, radical or accent node, mutably.
1644    #[inline]
1645    pub fn noad_mut(&mut self, p: NodeRef) -> Option<&mut NoadData> {
1646        match &mut self.at_mut(p).data {
1647            NodeData::Noad(n) => Some(n),
1648            _ => None,
1649        }
1650    }
1651
1652    /// The list of a `sub_box` or `sub_mlist` kernel (null otherwise).
1653    pub fn list_ptr_kernel(&self, p: NodeRef) -> NodeRef {
1654        if p.is_null() {
1655            return NodeRef::NULL;
1656        }
1657        match &self.at(p).data {
1658            NodeData::SubList { head } => *head,
1659            _ => NodeRef::NULL,
1660        }
1661    }
1662
1663    /// Sets the list of a `sub_box` or `sub_mlist` kernel.
1664    pub fn set_kernel_list(&mut self, p: NodeRef, l: NodeRef) {
1665        if p.is_null() {
1666            return;
1667        }
1668        if let NodeData::SubList { head } = &mut self.at_mut(p).data {
1669            *head = l;
1670        }
1671    }
1672
1673    /// Whether `p` is a noad that can carry scripts (`scripts_allowed`).
1674    pub fn scripts_allowed(&self, p: NodeRef) -> bool {
1675        matches!(self.at(p).data, NodeData::Noad(_))
1676            && matches!(self.id(p), id::NOAD | id::RADICAL | id::ACCENT)
1677    }
1678
1679    /// The number of nodes in the list starting at `p`.
1680    pub fn list_len(&self, mut p: NodeRef) -> usize {
1681        let mut n = 0;
1682        while !p.is_null() {
1683            n += 1;
1684            p = self.next(p);
1685        }
1686        n
1687    }
1688}
1689
1690/// The glue set ratio `num / den` as LuaTeX stores it: the quotient is
1691/// computed in double precision and kept as a C `float` (observed through
1692/// `tex.box[n].glue_set`). The rounding shows at shipout, where the ratio
1693/// multiplies the accumulated stretch or shrink (§625).
1694pub fn glue_ratio(num: i32, den: i32) -> f64 {
1695    f64::from((f64::from(num) / f64::from(den)) as f32)
1696}
1697
1698#[cfg(test)]
1699mod tests {
1700    use super::*;
1701
1702    #[test]
1703    fn glue_ratio_is_single_precision() {
1704        // `\hbox to 1021537sp{\hskip 0pt plus 873808sp}` in LuaTeX.
1705        assert_eq!(glue_ratio(1021537, 873808), 1.1690634489059448);
1706    }
1707
1708    #[test]
1709    fn alloc_link_copy_flush() {
1710        let mut n = Nodes::new();
1711        let b = n.new_null_box(true);
1712        let k = n.new_kern(5, kern_sub::USER);
1713        let g = n.new_glue(Glue::fixed(7), glue_sub::USER);
1714        n.link(k, g);
1715        n.box_data_mut(b).unwrap().head = k;
1716        assert_eq!(n.in_use, 3);
1717        let c = n.copy_node(b);
1718        assert_eq!(n.in_use, 6);
1719        let ch = n.box_data(c).unwrap().head;
1720        assert_ne!(ch, k);
1721        assert_eq!(n.get(n.next(ch)).data, n.get(g).data);
1722        n.flush_list(c);
1723        n.flush_list(b);
1724        assert_eq!(n.in_use, 0);
1725    }
1726
1727    /// A list that Lua code linked into a loop: walks that are bounded
1728    /// anyway end, and flushing it reports the node freed twice.
1729    #[test]
1730    fn circular_list() {
1731        let mut n = Nodes::new();
1732        let a = n.new_kern(1, kern_sub::USER);
1733        let b = n.new_glue(Glue::fixed(2), glue_sub::USER);
1734        n.link(a, b);
1735        n.link(b, a);
1736        assert_eq!(n.bounded_tail(a), None);
1737        let c = n.copy_list(a);
1738        assert!(!c.is_null());
1739        assert_eq!(n.double_free, None);
1740        n.flush_list(a);
1741        assert_eq!(n.double_free, Some((id::KERN, a.0)));
1742        assert_eq!(type_name(id::KERN), "kern");
1743    }
1744}