1use crate::Engine;
5use crate::cmd::Cmd;
6use crate::eqtb::{Loc, MU_GLUE_OFFSET, NUM_FAMILIES, Val};
7use crate::glue::Glue;
8use crate::maincontrol::Mode;
9use crate::nodes::NodeRef;
10
11impl Engine {
12 pub fn print_glue(&mut self, d: i32, order: u8, s: Option<&str>) {
15 self.printer.print_scaled(d);
16 if order > 4 {
17 self.printer.print_str("foul");
18 } else if order > 0 {
19 self.printer.print_str("fi");
20 for _ in 1..order {
21 self.printer.print_char('l' as u32);
22 }
23 } else if let Some(s) = s {
24 self.printer.print_str(s);
25 }
26 }
27
28 pub fn print_spec(&mut self, g: &Glue, s: Option<&str>) {
30 self.printer.print_scaled(g.width);
31 if let Some(s) = s {
32 self.printer.print_str(s);
33 }
34 if g.stretch != 0 {
35 self.printer.print_str(" plus ");
36 self.print_glue(g.stretch, g.stretch_order, s);
37 }
38 if g.shrink != 0 {
39 self.printer.print_str(" minus ");
40 self.print_glue(g.shrink, g.shrink_order, s);
41 }
42 }
43
44 pub fn show_eqtb(&mut self, loc: Loc) {
46 match loc {
47 Loc::Cs(cs) => {
48 self.sprint_cs(cs);
49 self.printer.print_char('=' as u32);
50 let m = self.eqtb.meaning(cs).clone();
51 if m.cmd.is_call() && m.mac.as_ref().is_some_and(|mac| mac.protected) {
52 self.print_esc_str("protected");
53 if m.cmd == Cmd::Call {
54 self.printer.print_char(' ' as u32);
55 }
56 }
57 self.print_cmd_chr(m.cmd, m.chr);
58 if m.cmd >= Cmd::Call
59 && let Some(mac) = m.mac
60 {
61 self.printer.print_char(':' as u32);
62 let t = mac.tokens.clone();
63 self.show_token_list(&t, 0, t.len(), 32);
64 }
65 }
66 Loc::IntPar(i) => {
67 self.print_cmd_chr(Cmd::AssignInt, i);
68 self.printer.print_char('=' as u32);
69 self.printer.print_int(self.eqtb.int(i).into());
70 }
71 Loc::Count(n) => {
72 self.print_esc_str("count");
73 self.printer.print_int(n.into());
74 self.printer.print_char('=' as u32);
75 self.printer
76 .print_int(self.eqtb.count[n as usize].val.into());
77 }
78 Loc::Attr(n) => {
79 self.print_esc_str("attribute");
80 self.printer.print_int(n.into());
81 self.printer.print_char('=' as u32);
82 self.printer
83 .print_int(self.eqtb.attr[n as usize].val.into());
84 }
85 Loc::DimenPar(i) => {
86 self.print_cmd_chr(Cmd::AssignDimen, i);
87 self.printer.print_char('=' as u32);
88 self.printer.print_scaled(self.eqtb.dimen(i));
89 self.printer.print_str("pt");
90 }
91 Loc::Dimen(n) => {
92 self.print_esc_str("dimen");
93 self.printer.print_int(n.into());
94 self.printer.print_char('=' as u32);
95 self.printer.print_scaled(self.eqtb.dimen[n as usize].val);
96 self.printer.print_str("pt");
97 }
98 Loc::GluePar(i) => {
99 let mu = i >= MU_GLUE_OFFSET;
100 if mu {
101 self.print_cmd_chr(Cmd::AssignMuGlue, i - MU_GLUE_OFFSET);
102 } else {
103 self.print_cmd_chr(Cmd::AssignGlue, i);
104 }
105 self.printer.print_char('=' as u32);
106 let g = self.eqtb.glue(i);
107 self.print_spec(&g, Some(if mu { "mu" } else { "pt" }));
108 }
109 Loc::Skip(n) | Loc::MuSkip(n) => {
110 let mu = matches!(loc, Loc::MuSkip(_));
111 self.print_esc_str(if mu { "muskip" } else { "skip" });
112 self.printer.print_int(n.into());
113 self.printer.print_char('=' as u32);
114 let g = if mu {
115 self.eqtb.muskip[n as usize].val
116 } else {
117 self.eqtb.skip[n as usize].val
118 };
119 self.print_spec(&g, Some(if mu { "mu" } else { "pt" }));
120 }
121 Loc::ToksPar(_) | Loc::Toks(_) => {
122 if let Loc::Toks(n) = loc {
123 self.print_esc_str("toks");
124 self.printer.print_int(n.into());
125 } else if let Loc::ToksPar(i) = loc {
126 self.print_cmd_chr(Cmd::AssignToks, i);
127 }
128 self.printer.print_char('=' as u32);
129 if let Val::Toks(t) = self.eqtb.get(loc) {
130 self.show_token_list(&t, 0, t.len(), 32);
131 }
132 }
133 Loc::Box(n) => {
134 self.print_esc_str("box");
135 self.printer.print_int(n.into());
136 self.printer.print_char('=' as u32);
137 let b = self.eqtb.boxes[n as usize].val;
138 if b.is_null() {
139 self.printer.print_str("void");
140 } else {
141 self.depth_threshold = 0;
142 self.breadth_max = 1;
143 self.show_box1(b);
144 }
145 }
146 Loc::CurFont => {
147 self.printer.print_str("current font");
148 self.printer.print_char('=' as u32);
149 let cs = self.font_id_cs(self.eqtb.cur_font.val);
150 let name = self.hash.name(cs).to_vec();
151 self.print_esc_bytes(&name);
152 }
153 Loc::MathFont(i) => {
154 let size = i / NUM_FAMILIES as u32;
155 let fam = i % NUM_FAMILIES as u32;
156 self.print_cmd_chr(Cmd::DefFamily, size);
157 self.printer.print_int(fam.into());
158 self.printer.print_char('=' as u32);
159 let cs = self.font_id_cs(self.eqtb.math_font[i as usize].val);
160 let name = self.hash.name(cs).to_vec();
161 self.print_esc_bytes(&name);
162 }
163 Loc::ParShape => {
164 self.print_esc_str("parshape");
165 self.printer.print_char('=' as u32);
166 let n = self.eqtb.par_shape.val.as_ref().map_or(0, |s| s.len() / 2);
167 self.printer.print_int(n as i64);
168 }
169 Loc::EtexShape(i) => {
170 self.print_cmd_chr(Cmd::SetEtexShape, i);
171 self.printer.print_char('=' as u32);
172 let n = self.eqtb.etex_shape[i as usize]
173 .val
174 .as_ref()
175 .map_or(0, |s| s.len());
176 self.printer.print_int(n as i64);
177 }
178 Loc::Cat { table, ch } => {
179 self.print_esc_str("catcode");
180 self.printer.print_int(ch.into());
181 self.printer.print_char('=' as u32);
182 self.printer
183 .print_int(self.eqtb.catcode_in(table, ch).into());
184 }
185 Loc::Code(kind, ch) => {
186 use crate::eqtb::CodeKind as K;
187 let name = match kind {
188 K::Lc => "lccode",
189 K::Uc => "uccode",
190 K::Sf => "sfcode",
191 K::Math => "mathcode",
192 K::Del => "delcode",
193 };
194 self.print_esc_str(name);
195 self.printer.print_int(ch.into());
196 self.printer.print_char('=' as u32);
197 self.printer.print_int(self.eqtb.code_table(kind).get(ch));
198 }
199 Loc::MathParam(i) => {
200 self.print_cmd_chr(Cmd::SetMathParam, i / 8);
201 self.printer.print_int((i % 8).into());
202 self.printer.print_char('=' as u32);
203 if let Val::Int(v) = self.eqtb.get(loc) {
204 self.printer.print_scaled(v as i32);
205 self.printer.print_str("pt");
206 }
207 }
208 Loc::LocalCount(i) => {
209 self.print_esc_str(if i == 0 {
210 "nolocalwhatsits"
211 } else {
212 "nolocaldirs"
213 });
214 self.printer.print_char('=' as u32);
215 self.printer
216 .print_int(self.eqtb.local_count[i as usize].val.into());
217 }
218 Loc::DirPar(i) => {
219 const NAMES: [&str; 6] = [
221 "pagedir", "bodydir", "pardir", "textdir", "mathdir", "linedir",
222 ];
223 self.print_esc_str(NAMES.get(i as usize).copied().unwrap_or("textdir"));
224 self.printer.print_char(' ' as u32);
225 let d = self.eqtb.dir_par[i as usize].val;
226 self.printer
227 .print_str(crate::showbox::DIR_NAMES[d.rem_euclid(4) as usize]);
228 }
229 Loc::LocalBox(i) => {
230 self.print_esc_str(if i == 0 {
231 "localleftbox"
232 } else {
233 "localrightbox"
234 });
235 self.printer.print_char('=' as u32);
236 let b = self.eqtb.local_box[i as usize].val;
237 if b.is_null() {
238 self.printer.print_str("void");
239 } else {
240 self.depth_threshold = 0;
241 self.breadth_max = 1;
242 self.show_box1(b);
243 }
244 }
245 }
246 }
247
248 pub fn show_box(&mut self, p: NodeRef) {
251 self.show_box_param_limits();
252 self.show_node_list(p);
253 self.printer.print_ln();
254 }
255
256 pub fn show_box1(&mut self, p: NodeRef) {
258 self.show_node_list(p);
259 }
260
261 pub fn show_save_groups(&mut self) {
269 use crate::save::{GroupCode, SaveEntry};
270 let (v_level, v_group, v_boundary) = (
271 self.save.cur_level,
272 self.save.cur_group,
273 self.save.cur_boundary,
274 );
275 let saved = |e: &Engine, b: usize, k: usize| -> i64 {
278 match b.checked_sub(k).and_then(|i| e.save.stack.get(i)) {
279 Some(SaveEntry::Value(v)) => *v,
280 _ => 0,
281 }
282 };
283 let mode_of = |e: &Engine, p: usize| e.nest.get(p).map(|l| l.mode);
284 let mut p = self.nest.len() - 1;
285 let mut a: i32 = 1;
286 self.printer.print_nl("");
287 self.printer.print_ln();
288 self.save.cur_level = self.save.cur_level.saturating_sub(1);
289 loop {
290 self.printer.print_nl("### ");
291 self.print_group(true);
292 let group = self.save.cur_group;
293 if group == GroupCode::BottomLevel {
294 break;
295 }
296 let b = self.save.cur_boundary;
297 let mut m;
298 loop {
299 m = mode_of(self, p).unwrap_or(Mode::Vertical);
300 if p > 0 {
301 p -= 1;
302 } else {
303 m = Mode::Vertical;
304 }
305 if m != Mode::Horizontal {
306 break;
307 }
308 }
309 self.printer.print_str(" (");
310 enum Tail {
313 Box(&'static str, bool),
314 Brace,
315 Close,
316 }
317 let tail = match group {
318 GroupCode::Simple => {
319 p += 1;
320 Tail::Brace
321 }
322 GroupCode::Hbox | GroupCode::AdjustedHbox => Tail::Box("hbox", true),
323 GroupCode::Vbox => Tail::Box("vbox", true),
324 GroupCode::Vtop => Tail::Box("vtop", true),
325 GroupCode::Align => {
326 if a == 0 {
327 a = 1;
328 Tail::Box(
329 if m == Mode::InternalVertical {
330 "halign"
331 } else {
332 "valign"
333 },
334 false,
335 )
336 } else {
337 if a == 1 {
338 self.printer.print_str("align entry");
339 } else {
340 self.print_esc_str("cr");
341 }
342 if p as i32 >= a {
343 p = (p as i32 - a) as usize;
344 }
345 a = 0;
346 Tail::Close
347 }
348 }
349 GroupCode::NoAlign => {
350 p += 1;
351 a = -1;
352 self.print_esc_str("noalign");
353 Tail::Brace
354 }
355 GroupCode::Output => {
356 self.print_esc_str("output");
357 Tail::Close
358 }
359 GroupCode::Math => Tail::Brace,
360 GroupCode::Disc | GroupCode::MathChoice => {
361 self.print_esc_str(if group == GroupCode::Disc {
362 "discretionary"
363 } else {
364 "mathchoice"
365 });
366 let n = saved(self, b, 1);
367 for i in 1..=3 {
368 if i <= n {
369 self.printer.print_str("{}");
370 }
371 }
372 Tail::Brace
373 }
374 GroupCode::Insert => {
375 let n = match self.save.stack.get(b) {
376 Some(SaveEntry::Boundary { line, .. }) => i64::from(*line),
377 _ => 0,
378 };
379 if n == 255 {
380 self.print_esc_str("vadjust");
381 } else {
382 self.print_esc_str("insert");
383 self.printer.print_int(n);
384 }
385 Tail::Brace
386 }
387 GroupCode::Vcenter => Tail::Box("vcenter", false),
388 GroupCode::SemiSimple => {
389 p += 1;
390 self.print_esc_str("begingroup");
391 Tail::Close
392 }
393 GroupCode::MathShift => {
394 if m == Mode::DisplayMath {
395 self.printer.print_char('$' as u32);
396 } else if mode_of(self, p) == Some(Mode::DisplayMath) {
397 self.print_cmd_chr(crate::cmd::Cmd::EqNo, saved(self, b, 1) as u32);
398 self.printer.print_str(")");
399 self.save_groups_next();
400 continue;
401 }
402 self.printer.print_char('$' as u32);
403 Tail::Close
404 }
405 GroupCode::MathLeft => {
406 let left = self
407 .nest
408 .get(p + 1)
409 .map(|l| l.delim_ptr)
410 .is_some_and(|d| !d.is_null() && self.nodes.subtype(d) == 1);
411 self.print_esc_str(if left { "left" } else { "middle" });
412 Tail::Close
413 }
414 _ => Tail::Close,
415 };
416 if let Tail::Box(s, context) = tail {
417 let off = usize::from(matches!(
420 group,
421 GroupCode::Hbox | GroupCode::AdjustedHbox | GroupCode::Vbox | GroupCode::Vtop
422 ));
423 if context {
424 let i = saved(self, b, 2 + off);
426 match crate::build::ctx_from_i64(i) {
427 crate::maincontrol::BoxContext::Shift(0) => {}
428 crate::maincontrol::BoxContext::Shift(d) => {
429 let vertical = mode_of(self, p).is_some_and(|m| {
430 matches!(m, Mode::Vertical | Mode::InternalVertical)
431 });
432 let cmd = if vertical {
433 crate::cmd::Cmd::Hmove
434 } else {
435 crate::cmd::Cmd::Vmove
436 };
437 self.print_cmd_chr(cmd, u32::from(d < 0));
438 self.printer.print_scaled(d.abs());
439 self.printer.print_str("pt");
440 }
441 crate::maincontrol::BoxContext::SetBox(n) => {
442 self.print_esc_str("setbox");
443 self.printer.print_int(i64::from(n));
444 self.printer.print_char('=' as u32);
445 }
446 crate::maincontrol::BoxContext::GlobalSetBox(n) => {
447 self.print_esc_str("global");
448 self.print_esc_str("setbox");
449 self.printer.print_int(i64::from(n));
450 self.printer.print_char('=' as u32);
451 }
452 crate::maincontrol::BoxContext::Shipout => self.print_esc_str("shipout"),
453 crate::maincontrol::BoxContext::Leaders(sub) => {
454 self.print_cmd_chr(crate::cmd::Cmd::LeaderShip, u32::from(sub))
455 }
456 crate::maincontrol::BoxContext::Lua => {}
457 }
458 }
459 self.print_esc_str(s);
460 let (code, d) = (saved(self, b, 3 + off), saved(self, b, 2 + off));
463 if d != 0 {
464 self.printer.print_char(' ' as u32);
465 self.printer
466 .print_str(if code == 0 { "to" } else { "spread" });
467 self.printer.print_scaled(d as i32);
468 self.printer.print_str("pt");
469 }
470 self.printer.print_char('{' as u32);
471 } else if let Tail::Brace = tail {
472 self.printer.print_char('{' as u32);
473 }
474 self.printer.print_char(')' as u32);
475 self.save_groups_next();
476 }
477 self.save.cur_level = v_level;
478 self.save.cur_group = v_group;
479 self.save.cur_boundary = v_boundary;
480 }
481
482 fn save_groups_next(&mut self) {
484 self.save.cur_level = self.save.cur_level.saturating_sub(1);
485 if let Some(crate::save::SaveEntry::Boundary {
486 group, boundary, ..
487 }) = self.save.stack.get(self.save.cur_boundary)
488 {
489 self.save.cur_group = *group;
490 self.save.cur_boundary = *boundary;
491 } else {
492 self.save.cur_group = crate::save::GroupCode::BottomLevel;
493 }
494 }
495}