Skip to main content

retex_engine/
lib.rs

1//! The retex TeX engine: a clean-room implementation of LuaTeX's
2//! typesetting machine.
3//!
4//! The [`Engine`] holds all state of a TeX run. Its methods are spread
5//! over modules following the parts of *TeX: The Program*:
6//!
7//! * [`print`] and [`error`] — terminal and log output, error recovery;
8//! * [`hash`], [`eqtb`], [`save`] — control sequences, equivalents, grouping;
9//! * [`input`] — the input stack and the tokenizer (`get_next`);
10//! * [`expand`], [`cond`], [`scan`] — expansion, conditionals, scanning;
11//! * [`prefixed`] — assignments;
12//! * [`maincontrol`] — the chief executive.
13
14#![forbid(unsafe_code)]
15#![allow(clippy::too_many_lines)]
16
17pub mod align;
18pub mod backend;
19pub mod build;
20pub mod clock;
21pub mod cmd;
22mod cmd_gen;
23pub mod codes;
24pub mod cond;
25pub mod diag;
26pub mod dirs;
27pub mod display;
28pub mod dvi;
29pub mod eqtb;
30pub mod error;
31pub mod expand;
32pub mod expansion;
33pub mod files;
34pub mod fonts;
35pub mod format;
36pub mod glue;
37pub mod hash;
38pub mod hmode;
39pub mod hyph;
40pub mod image;
41pub mod init;
42pub mod input;
43pub mod linebreak;
44pub mod maincontrol;
45pub mod math;
46pub mod mathdisplay;
47pub mod mathparams;
48pub mod meaning;
49pub mod misc;
50pub mod mlist;
51pub mod nodeproc;
52pub mod nodes;
53pub mod pack;
54pub mod page;
55pub mod pdfext;
56pub(crate) mod pdfship;
57pub mod prefixed;
58pub mod primitives;
59pub mod primitives_gen;
60pub mod print;
61pub mod protrusion;
62pub mod save;
63pub mod scan;
64pub mod shipout;
65pub mod showbox;
66pub mod snapshot;
67pub mod the;
68pub mod timing;
69pub mod token;
70pub mod typeset;
71pub mod utf8x;
72
73use cmd::Cmd;
74use cond::CondState;
75use eqtb::Eqtb;
76use error::ErrorState;
77use files::FileState;
78use hash::{CsId, CsTable};
79use input::InputState;
80use print::Printer;
81use save::SaveStack;
82use token::Token;
83
84/// Callbacks from the engine into an embedded Lua interpreter.
85///
86/// The engine does not depend on Lua. `\directlua` and related primitives
87/// call this trait, which `retex-lua` implements. The host is shared
88/// (`Rc`), so nested calls (Lua → TeX → Lua) reach it too.
89pub trait LuaHost {
90    /// Runs a chunk of Lua code for `\directlua` (or `\latelua`, etc.).
91    /// `name` is the chunk name. Text printed with `tex.print` must be
92    /// pushed back into the engine's input by the host.
93    fn run(&self, engine: &mut Engine, name: &str, code: &[u8]) -> Result<(), String>;
94
95    /// Data to store in the format file (e.g. `lua.bytecode`).
96    fn dump(&self, _engine: &mut Engine) -> Vec<u8> {
97        Vec::new()
98    }
99
100    /// Restores data stored by [`dump`](Self::dump).
101    fn undump(&self, _engine: &mut Engine, _data: &[u8]) -> Result<(), String> {
102        Ok(())
103    }
104
105    /// Calls callback `name` without arguments, ignoring its results
106    /// (`pre_dump`, `stop_run`, …). Does nothing if none is registered.
107    fn call_simple(&self, _engine: &mut Engine, _name: &str) -> Result<(), String> {
108        Ok(())
109    }
110
111    /// Whether a Lua function is registered for callback `name`.
112    fn has_callback(&self, _name: &str) -> bool {
113        false
114    }
115
116    /// Calls the `define_font` callback. Returns the id of the font it
117    /// defined, or `None` if the font could not be loaded.
118    fn define_font(&self, _engine: &mut Engine, _name: &[u8], _size: i32) -> Option<u32> {
119        None
120    }
121
122    /// Calls a node-list filter callback (`pre_linebreak_filter`,
123    /// `hpack_filter`, `post_linebreak_filter`, …) on the list starting at
124    /// `head`. Returns the new head; [`ListFilter::Flush`] means the list
125    /// must be discarded.
126    fn list_filter(
127        &self,
128        _engine: &mut Engine,
129        _callback: &str,
130        head: nodes::NodeRef,
131        _group: &str,
132    ) -> ListFilter {
133        ListFilter::Keep(head)
134    }
135
136    /// Calls the `glyph_info` callback for glyph node `p`: `Some(text)` is
137    /// what to print, `None` means the callback returned no string.
138    fn glyph_info(&self, _engine: &mut Engine, _p: nodes::NodeRef) -> Option<Vec<u8>> {
139        None
140    }
141}
142
143/// Result of a node-list filter callback.
144#[derive(Clone, Copy, Debug, PartialEq, Eq)]
145pub enum ListFilter {
146    /// Continue with this list (possibly the original head).
147    Keep(nodes::NodeRef),
148    /// The callback returned `false`: discard the list.
149    Flush,
150}
151
152impl Engine {
153    /// Whether the Lua callback `name` is set.
154    pub fn has_callback(&self, name: &str) -> bool {
155        self.lua.as_ref().is_some_and(|h| h.has_callback(name))
156    }
157
158    /// Runs a node-list filter callback if one is registered (the hook
159    /// typesetting code calls at LuaTeX's callback points).
160    pub fn run_list_filter(
161        &mut self,
162        callback: &str,
163        head: nodes::NodeRef,
164        group: &str,
165    ) -> ListFilter {
166        match self.lua.clone() {
167            Some(h) if h.has_callback(callback) => h.list_filter(self, callback, head, group),
168            _ => ListFilter::Keep(head),
169        }
170    }
171}
172
173impl Engine {
174    /// Runs the `glyph_info` callback for glyph `p` when one is set:
175    /// `None` without a callback, `Some(None)` when it returned no string.
176    pub fn run_glyph_info(&mut self, p: nodes::NodeRef) -> Option<Option<Vec<u8>>> {
177        match self.lua.clone() {
178            Some(h) if h.has_callback("glyph_info") => Some(h.glyph_info(self, p)),
179            _ => None,
180        }
181    }
182}
183
184/// A one-shot callback into the host, run with the engine borrowed.
185pub type EngineHook = Box<dyn FnOnce(&mut Engine)>;
186
187/// The global state of a TeX run.
188pub struct Engine {
189    /// Control-sequence names.
190    pub hash: CsTable,
191    /// The table of equivalents.
192    pub eqtb: Eqtb,
193    /// The save stack and grouping state.
194    pub save: SaveStack,
195    /// The input stack.
196    pub input: InputState,
197    /// Terminal, log and `\write` output.
198    pub printer: Printer,
199    /// Error-handling state.
200    pub err: ErrorState,
201    /// Conditionals.
202    pub cond: CondState,
203    /// Files: `\openin`/`\read`, `\openout`, input file stack.
204    pub files: FileState,
205    /// The file finder.
206    pub kpse: Option<retex_kpath::Shared>,
207    /// The Lua interpreter, when available.
208    pub lua: Option<std::rc::Rc<dyn LuaHost>>,
209
210    /// The current command code (`cur_cmd`).
211    pub cur_cmd: Cmd,
212    /// The current modifier (`cur_chr`).
213    pub cur_chr: u32,
214    /// The current control sequence (`cur_cs`); [`hash::NO_CS`] for
215    /// characters.
216    pub cur_cs: CsId,
217    /// The current token (`cur_tok`).
218    pub cur_tok: Token,
219
220    /// The value most recently scanned (`cur_val`).
221    pub cur_val: i32,
222    /// The glue most recently scanned, when `cur_val_level` is glue.
223    pub cur_glue: glue::Glue,
224    /// The token list most recently scanned, when `cur_val_level` is tokens.
225    pub cur_toks: Option<token::TokenList>,
226    /// The level of `cur_val`.
227    pub cur_val_level: scan::ValLevel,
228
229    /// Well-known control sequences.
230    pub cs: init::KnownCs,
231    /// Whether the run is in IniTeX mode.
232    pub ini_version: bool,
233    /// Shell escape mode (see [`init::Options::shell_escape`]).
234    pub shell_escape: u8,
235    /// The format identification printed in the banner.
236    pub format_ident: String,
237    /// The job name, once known.
238    pub job_name: Option<Vec<u8>>,
239    /// Token saved by `\afterassignment` (`after_token`).
240    pub after_token: Token,
241    /// Number of nested expansions (LuaTeX's `expand_depth_count`).
242    pub expand_depth: u32,
243    /// The current mode (copy of the innermost nest level's mode).
244    pub mode: maincontrol::Mode,
245    /// The mode last shown by `show_cur_cmd_chr`.
246    pub shown_mode: maincontrol::Mode,
247    /// The format name (`\formatname`).
248    pub format_name: String,
249    /// Node memory.
250    pub nodes: nodes::Nodes,
251    /// The semantic nest; the last entry is the current list.
252    pub nest: Vec<maincontrol::ListState>,
253    /// Loaded fonts.
254    pub fonts: fonts::FontTable,
255    /// The `long_state` of the macro being called.
256    pub long_state: Cmd,
257    /// The control sequence being defined (for diagnostics).
258    pub def_ref_cs: CsId,
259    /// The partial token list printed by `runaway`.
260    pub runaway_list: Vec<Token>,
261    /// The control sequence last made by `\csname` (`\lastnamedcs`).
262    pub last_cs_name: CsId,
263    /// Frozen copies made by `\primitive`.
264    pub frozen_prims: std::collections::HashMap<Vec<u8>, CsId>,
265    /// Mark texts.
266    pub marks: typeset::Marks,
267    /// Set when a `\read` line has been consumed.
268    pub input_read_finished: bool,
269    /// Whether the long `\errmessage` help has been shown.
270    pub long_help_seen: bool,
271    /// `\deadcycles`.
272    pub dead_cycles: i32,
273    /// `\insertpenalties`.
274    pub insert_penalties: i32,
275    /// `\badness`.
276    pub last_badness: i32,
277    /// Token lists show a macro parameter character once, not doubled
278    /// (`\luaescapestring`).
279    pub single_mac_param: bool,
280    /// The direction the next box made by `hpack` or `vpackage` gets
281    /// (from `dir`/`bdir` of a box specification), or -1. It is set before
282    /// packing because over- and underfull box reports show it.
283    pub pack_dir: i8,
284    /// `\randomseed`: the seed `\setrandomseed` last gave (its absolute
285    /// value), or the one chosen at startup.
286    pub random_seed: i32,
287    /// The state of the random number generator (METAFONT's, see
288    /// [`retex_arith::random`]) behind `\uniformdeviate`, `\normaldeviate`
289    /// and the `tex` library's randomizers.
290    pub randoms: retex_arith::random::Randoms,
291    /// `\lastxpos`.
292    pub last_x_pos: i32,
293    /// `\lastypos`.
294    pub last_y_pos: i32,
295    /// The magnification frozen by `prepare_mag`.
296    pub mag_set: i32,
297    /// The font dimension located by `find_font_dimen`.
298    pub font_dimen_loc: Option<(u32, u32)>,
299    /// `\showboxdepth` in effect while displaying boxes.
300    pub depth_threshold: i32,
301    /// `\showboxbreadth` in effect while displaying boxes.
302    pub breadth_max: i32,
303    /// The paragraph-ending control sequence (`\partokenname`).
304    pub par_token_cs: CsId,
305    /// Whether `\dump` ended the run.
306    pub dump_requested: bool,
307    /// The job's start time (`\time`, the banner, the PDF creation date).
308    pub start_time: clock::StartTime,
309    /// The page builder.
310    pub page: typeset::PageState,
311    /// Lua functions registered for `\luafunction` (by number).
312    pub lua_functions: std::collections::HashMap<i32, u32>,
313    /// Called to store the format when `\dump` is executed.
314    pub format_hook: Option<EngineHook>,
315    /// Called at the end of the run to finish output files.
316    pub finish_hook: Option<EngineHook>,
317    /// Non-immediate `\write`s waiting for shipout.
318    pub deferred_writes: Vec<(i32, token::TokenList)>,
319    /// Text directions opened in the current box: (group level,
320    /// direction), innermost last (see [`dirs`]).
321    pub text_dirs: Vec<(u16, i8)>,
322    /// Box resources to write after the page or form being written, one
323    /// list per open page or form.
324    pub pending_forms: Vec<Vec<u32>>,
325    /// Objects referred to (`\pdfextension refobj`) in the box resources
326    /// being written, one list per open form.
327    pub form_objects: Vec<Vec<pdfext::PdfObject>>,
328    /// The `text_dirs` of the enclosing boxes.
329    pub outer_text_dirs: Vec<Vec<(u16, i8)>>,
330    /// Contents of files written by `\write`, for the pass driver.
331    pub written_sink: files::WrittenSink,
332    /// State of the typesetting routines.
333    pub ts: typeset::TsState,
334    /// Tokens printed from Lua waiting to be pushed as a token list.
335    pub pending_lua_tokens: Option<Vec<Token>>,
336    /// Default output mode: errors and warnings recorded for the driver
337    /// (`None` in compatibility mode).
338    pub diagnostics: Option<Vec<diag::Diagnostic>>,
339    /// Image resources (`\saveimageresource`), in the order saved (their
340    /// indices are their object numbers).
341    pub images: Vec<image::ImageResource>,
342    /// `\lastsavedimageresourceindex`.
343    pub last_image_index: i32,
344    /// `\lastsavedimageresourcepages`.
345    pub last_image_pages: i32,
346    /// Box resources (`\saveboxresource`), in the order saved (their
347    /// indices are their object numbers).
348    pub box_resources: Vec<image::BoxResource>,
349    /// `\lastsavedboxresourceindex`.
350    pub last_box_index: i32,
351    /// Scratch buffer for macro arguments (reused by `macro_call`).
352    pub arg_scratch: Vec<Token>,
353    /// Scratch buffer for the argument list of a macro call.
354    pub param_scratch: Vec<token::TokenList>,
355    /// The file of each `\write` stream opened (see [`snapshot`]).
356    pub write_paths: Vec<Option<std::path::PathBuf>>,
357    /// The macro at whose expansion [`snapshot_hook`](Self::snapshot_hook)
358    /// runs (LaTeX's `\document`).
359    pub snapshot_cs: Option<CsId>,
360    /// Called once, when `snapshot_cs` is expanded (see [`snapshot`]).
361    pub snapshot_hook: Option<snapshot::Hook>,
362    /// The output file written at the end: name, pages and bytes.
363    pub output_written: Option<(String, u32, u64)>,
364    /// The line number reported while reading Lua output.
365    pub input_line_for_lua: i32,
366    /// Internal tokens made by `\pdfvariable`, by variable name.
367    pub backend_cs: std::collections::HashMap<&'static str, CsId>,
368    /// Nesting depth of local control loops (`tex.runtoks`).
369    pub local_level: u32,
370    /// The output backend (PDF writer), if any.
371    pub backend: Option<Box<dyn shipout::Backend>>,
372    /// The DVI writer (`\outputmode=0`).
373    pub dvi: dvi::DviWriter,
374    /// Hyphenation languages, by `\language` number.
375    pub languages: Vec<Option<retex_hyph::Language>>,
376    /// Document-level PDF data from `\pdfextension`.
377    pub pdf: pdfext::PdfState,
378    /// Current values of the colour stacks during shipout.
379    pub colorstack_values: std::collections::HashMap<i32, Vec<Vec<u8>>>,
380    /// Number of pages shipped out.
381    pub total_pages: u32,
382    /// Muglue values of `\Umath…` spacing parameters.
383    pub math_glues: Vec<glue::Glue>,
384    /// Whether the parameters set by the first formula are initialized
385    /// (see [`mathparams`]).
386    pub math_params_initialized: bool,
387}
388
389impl std::fmt::Debug for Engine {
390    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
391        f.debug_struct("Engine")
392            .field("cur_cmd", &self.cur_cmd)
393            .field("cur_chr", &self.cur_chr)
394            .finish_non_exhaustive()
395    }
396}