1use super::primitives::*;
2use crate::commands::methods::{IfxCmd, MacroParser, END_TEMPLATE, END_TEMPLATE_ROW};
3use crate::commands::{
4 ActiveConditional, CharOrPrimitive, CommandScope, Macro, MacroSignature, PrimitiveCommand,
5 ResolvedToken, TeXCommand,
6};
7use crate::engine::filesystem::{File, FileSystem};
8use crate::engine::fontsystem::Font;
9use crate::engine::fontsystem::FontSystem;
10use crate::engine::gullet::methods::CSOrActiveChar;
11use crate::engine::gullet::Gullet;
12use crate::engine::mouth::Mouth;
13use crate::engine::state::{GroupType, State};
14use crate::engine::stomach::methods::SplitResult;
15use crate::engine::stomach::{Stomach, TeXMode};
16use crate::engine::utils::outputs::Outputs;
17use crate::engine::{EngineReferences, EngineTypes, TeXEngine};
18use crate::tex::catcodes::{CategoryCode, CommandCode};
19use crate::tex::characters::{Character, CharacterMap};
20use crate::tex::nodes::boxes::{BoxInfo, BoxType, HBoxInfo, TeXBox, ToOrSpread, VBoxInfo};
21use crate::tex::nodes::horizontal::{HNode, HorizontalNodeListType};
22use crate::tex::nodes::math::{
23 Delimiter, EqNoPosition, MathAtom, MathChar, MathClass, MathKernel, MathNode, MathNodeList,
24 MathNodeListType, MathNucleus, UnresolvedMarkers, UnresolvedMathChoice,
25 UnresolvedMathFontStyle,
26};
27use crate::tex::nodes::vertical::{VNode, VerticalNodeListType};
28use crate::tex::nodes::{BoxTarget, LeaderType, ListTarget, NodeList, NodeTrait, WhatsitFunction};
29use crate::tex::numerics::TeXDimen;
30use crate::tex::numerics::{MuSkip, NumSet, Skip};
31use crate::tex::tokens::control_sequences::{CSHandler, ResolvedCSName};
32use crate::tex::tokens::token_lists::CharWrite;
33use crate::tex::tokens::token_lists::Otherize;
34use crate::tex::tokens::{StandardToken, Token};
35use crate::utils::errors::{TeXError, TeXResult};
36use crate::{add_node, expand_loop};
37use either::Either;
38use std::cmp::Ordering;
39use std::fmt::Write;
40
41pub fn accent<ET: EngineTypes>(
42 engine: &mut EngineReferences<ET>,
43 tk: ET::Token,
44) -> TeXResult<(), ET> {
45 let accent = engine.read_charcode(false, &tk)?;
46 crate::expand_loop!(engine,token,
47 ResolvedToken::Tk{char,code:CommandCode::Other|CommandCode::Letter} => {
48 ET::Stomach::add_node_h(engine,HNode::Accent {accent,char,font:engine.state.get_current_font().clone()});
49 return Ok(())
50 }
51 ResolvedToken::Cmd(Some(TeXCommand::Char {char,code:CommandCode::Other|CommandCode::Letter})) => {
52 ET::Stomach::add_node_h(engine,HNode::Accent {accent,char:*char,font:engine.state.get_current_font().clone()});
53 return Ok(())
54 }
55 ResolvedToken::Cmd(Some(TeXCommand::CharDef(char))) => {
56 ET::Stomach::add_node_h(engine,HNode::Accent {accent,char:*char,font:engine.state.get_current_font().clone()});
57 return Ok(())
58 }
59 _ => {
60 engine.requeue(token)?;
61 let tk = <ET::Token as Token>::from_char_cat(accent,CommandCode::Other);
62 ET::Stomach::do_char(engine,tk,accent,CommandCode::Other)?;
63 return Ok(())
64 }
65 );
66 engine.general_error("Unexpected token after accent command".to_string())
67}
68
69pub fn afterassignment<ET: EngineTypes>(
70 engine: &mut EngineReferences<ET>,
71 tk: ET::Token,
72) -> TeXResult<(), ET> {
73 let next = engine.need_next(true, &tk)?;
74 *engine.stomach.afterassignment() = Some(next);
75 Ok(())
76}
77
78pub fn aftergroup<ET: EngineTypes>(
79 engine: &mut EngineReferences<ET>,
80 tk: ET::Token,
81) -> TeXResult<(), ET> {
82 let next = engine.need_next(true, &tk)?;
83 engine.state.aftergroup(next);
84 Ok(())
85}
86
87pub fn begingroup<ET: EngineTypes>(
88 engine: &mut EngineReferences<ET>,
89 _tk: ET::Token,
90) -> TeXResult<(), ET> {
91 engine.state.push(
92 engine.aux,
93 GroupType::SemiSimple,
94 engine.mouth.line_number(),
95 );
96 Ok(())
97}
98pub fn endgroup<ET: EngineTypes>(
99 engine: &mut EngineReferences<ET>,
100 _tk: ET::Token,
101) -> TeXResult<(), ET> {
102 match engine.state.get_group_type() {
103 Some(GroupType::SemiSimple) => (),
104 _ => TeXError::missing_endgroup(engine.aux, engine.state, engine.mouth)?,
105 }
106 engine.state.pop(engine.aux, engine.mouth);
107 Ok(())
108}
109
110pub fn end<ET: EngineTypes>(
111 engine: &mut EngineReferences<ET>,
112 _tk: ET::Token,
113) -> TeXResult<(), ET> {
114 ET::Stomach::flush(engine)?;
115 engine.mouth.finish();
116 Ok(())
117}
118
119pub fn discretionary<ET: EngineTypes>(
120 engine: &mut EngineReferences<ET>,
121 tk: ET::Token,
122) -> TeXResult<(), ET> {
123 engine.skip_argument(&tk)?;
124 engine.skip_argument(&tk)?;
125 engine.skip_argument(&tk)
126 }
128
129pub fn endinput<ET: EngineTypes>(
130 engine: &mut EngineReferences<ET>,
131 _tk: ET::Token,
132) -> TeXResult<(), ET> {
133 engine.mouth.endinput(engine.aux, engine.state)?;
134 Ok(())
135}
136
137pub fn errorstopmode<ET: EngineTypes>(
138 _engine: &mut EngineReferences<ET>,
139 _tk: ET::Token,
140) -> TeXResult<(), ET> {
141 Ok(())
142}
143
144pub fn expandafter<ET: EngineTypes>(
145 engine: &mut EngineReferences<ET>,
146 tk: ET::Token,
147) -> TeXResult<(), ET> {
148 let first = engine.need_next(false, &tk)?;
149 let second = engine.need_next(false, &tk)?;
150 engine.expand(second)?;
151 engine.requeue(first)
152}
153
154pub fn catcode_get<ET: EngineTypes>(
155 engine: &mut EngineReferences<ET>,
156 tk: ET::Token,
157) -> TeXResult<ET::Int, ET> {
158 let char = engine.read_charcode(false, &tk)?;
159 let u: u8 = (*engine.state.get_catcode_scheme().get(char)).into();
160 Ok(ET::Int::from(u as i32))
161}
162pub fn catcode_set<ET: EngineTypes>(
163 engine: &mut EngineReferences<ET>,
164 tk: ET::Token,
165 globally: bool,
166) -> TeXResult<(), ET> {
167 let char = engine.read_charcode(false, &tk)?;
168 let val: i64 = engine.read_int(true, &tk)?.into();
169 if !(0..=15).contains(&val) {
170 return engine.general_error(format!("Illegal category code {val}"));
171 }
172 let cc: CategoryCode = (val as u8).try_into().unwrap();
173 engine.state.set_catcode(engine.aux, char, cc, globally);
174 Ok(())
175}
176
177pub fn sfcode_get<ET: EngineTypes>(
178 engine: &mut EngineReferences<ET>,
179 tk: ET::Token,
180) -> TeXResult<ET::Int, ET> {
181 let char = engine.read_charcode(false, &tk)?;
182 let u: u16 = engine.state.get_sfcode(char);
183 Ok(ET::Int::from(u as i32))
184}
185pub fn sfcode_set<ET: EngineTypes>(
186 engine: &mut EngineReferences<ET>,
187 tk: ET::Token,
188 globally: bool,
189) -> TeXResult<(), ET> {
190 let char = engine.read_charcode(false, &tk)?;
191 let val: i64 = engine.read_int(true, &tk)?.into();
192 if !(0..=32767).contains(&val) {
193 return engine.general_error(format!("Illegal spacefactor code {val}"));
194 }
195 let sf = val as u16;
196 engine.state.set_sfcode(engine.aux, char, sf, globally);
197 Ok(())
198}
199
200pub fn spacefactor_get<ET: EngineTypes>(
201 engine: &mut EngineReferences<ET>,
202 _tk: ET::Token,
203) -> TeXResult<ET::Int, ET> {
204 Ok(ET::Int::from(engine.stomach.data_mut().spacefactor))
205}
206pub fn spacefactor_set<ET: EngineTypes>(
207 engine: &mut EngineReferences<ET>,
208 tk: ET::Token,
209 _globally: bool,
210) -> TeXResult<(), ET> {
211 let val = match engine.read_int(true, &tk)?.try_into() {
212 Ok(v) => v,
213 _ => return engine.general_error("Illegal spacefactor code".to_string()),
214 };
215 engine.stomach.data_mut().spacefactor = val;
216 Ok(())
217}
218
219pub fn parshape_get<ET: EngineTypes>(
220 engine: &mut EngineReferences<ET>,
221 _tk: ET::Token,
222) -> TeXResult<ET::Int, ET> {
223 Ok(ET::Int::from(engine.state.get_parshape().len() as i32))
224}
225pub fn parshape_set<ET: EngineTypes>(
226 engine: &mut EngineReferences<ET>,
227 tk: ET::Token,
228 globally: bool,
229) -> TeXResult<(), ET> {
230 let len = engine.read_int(false, &tk)?.into();
231 if len < 0 {
232 return engine.general_error(format!("Illegal parshape length {len}"));
233 }
234 let mut shape = Vec::with_capacity(len as usize);
235 for _ in 0..len {
236 let a = engine.read_dim(false, &tk)?;
237 let b = engine.read_dim(false, &tk)?;
238 shape.push((a, b))
239 }
240 engine.state.set_parshape(engine.aux, shape, globally);
241 Ok(())
242}
243
244pub fn lccode_get<ET: EngineTypes>(
245 engine: &mut EngineReferences<ET>,
246 tk: ET::Token,
247) -> TeXResult<ET::Int, ET> {
248 let char = engine.read_charcode(false, &tk)?;
249 let u = engine.state.get_lccode(char).into();
250 match ET::Int::try_from(u as i64) {
251 Ok(v) => Ok(v),
252 _ => {
253 engine.general_error(format!("Illegal lowercase code {u}"))?;
254 Ok(ET::Int::from(0))
255 }
256 }
257}
258pub fn lccode_set<ET: EngineTypes>(
259 engine: &mut EngineReferences<ET>,
260 tk: ET::Token,
261 globally: bool,
262) -> TeXResult<(), ET> {
263 let char = engine.read_charcode(false, &tk)?;
264 let val = engine.read_charcode(true, &tk)?;
265 engine.state.set_lccode(engine.aux, char, val, globally);
266 Ok(())
267}
268
269pub fn uccode_get<ET: EngineTypes>(
270 engine: &mut EngineReferences<ET>,
271 tk: ET::Token,
272) -> TeXResult<ET::Int, ET> {
273 let char = engine.read_charcode(false, &tk)?;
274 let u = engine.state.get_uccode(char).into();
275 match ET::Int::try_from(u as i64) {
276 Ok(v) => Ok(v),
277 _ => {
278 engine.general_error(format!("Illegal uppercase code {u}"))?;
279 Ok(ET::Int::from(0))
280 }
281 }
282}
283pub fn uccode_set<ET: EngineTypes>(
284 engine: &mut EngineReferences<ET>,
285 tk: ET::Token,
286 globally: bool,
287) -> TeXResult<(), ET> {
288 let char = engine.read_charcode(false, &tk)?;
289 let val = engine.read_charcode(true, &tk)?;
290 engine.state.set_uccode(engine.aux, char, val, globally);
291 Ok(())
292}
293
294pub fn mathcode_get<ET: EngineTypes>(
295 engine: &mut EngineReferences<ET>,
296 tk: ET::Token,
297) -> TeXResult<ET::Int, ET> {
298 let char = engine.read_charcode(false, &tk)?;
299 let u = engine.state.get_mathcode(char);
300 match ET::Int::try_from(u as i64) {
301 Ok(v) => Ok(v),
302 _ => {
303 engine.general_error(format!("Illegal mathcode {u}"))?;
304 Ok(ET::Int::from(0))
305 }
306 }
307}
308pub fn mathcode_set<ET: EngineTypes>(
309 engine: &mut EngineReferences<ET>,
310 tk: ET::Token,
311 globally: bool,
312) -> TeXResult<(), ET> {
313 let char = engine.read_charcode(false, &tk)?;
314 let mut val = engine.read_int(true, &tk)?.into();
315 if val < 0 || val > 32768 {
316 engine.general_error(format!("Illegal mathcode {val}"))?;
317 val = 0;
318 }
319 engine
320 .state
321 .set_mathcode(engine.aux, char, val as u32, globally);
322 Ok(())
323}
324
325pub fn delcode_get<ET: EngineTypes>(
326 engine: &mut EngineReferences<ET>,
327 tk: ET::Token,
328) -> TeXResult<ET::Int, ET> {
329 let char = engine.read_charcode(false, &tk)?;
330 Ok(engine.state.get_delcode(char))
331}
332pub fn delcode_set<ET: EngineTypes>(
333 engine: &mut EngineReferences<ET>,
334 tk: ET::Token,
335 globally: bool,
336) -> TeXResult<(), ET> {
337 let char = engine.read_charcode(false, &tk)?;
338 let val = engine.read_int(true, &tk)?;
339 engine.state.set_delcode(engine.aux, char, val, globally);
340 Ok(())
341}
342
343pub fn chardef<ET: EngineTypes>(
344 engine: &mut EngineReferences<ET>,
345 tk: ET::Token,
346 globally: bool,
347) -> TeXResult<(), ET> {
348 let name = engine.read_control_sequence(&tk)?;
349 let char = engine.read_charcode(true, &tk)?;
350 let cmd = TeXCommand::CharDef(char);
351 engine.set_command(&name, Some(cmd), globally);
352 Ok(())
353}
354
355pub fn r#char<ET: EngineTypes>(
356 engine: &mut EngineReferences<ET>,
357 tk: ET::Token,
358) -> TeXResult<(), ET> {
359 let char = engine.read_charcode(false, &tk)?;
360 match engine.stomach.data_mut().mode() {
361 TeXMode::DisplayMath | TeXMode::InlineMath => ET::Stomach::do_char_in_math(engine, char),
362 _ => {
363 let tk = <ET::Token as Token>::from_char_cat(char, CommandCode::Other);
364 ET::Stomach::do_char(engine, tk, char, CommandCode::Other)
365 }
366 }
367}
368
369pub fn csname<ET: EngineTypes>(
370 engine: &mut EngineReferences<ET>,
371 _tk: ET::Token,
372) -> TeXResult<(), ET> {
373 let name = engine.read_csname()?;
374 if engine.state.get_command(&name).is_none() {
375 engine.state.set_command(
376 engine.aux,
377 name.clone(),
378 Some(TeXCommand::Primitive {
379 name: PRIMITIVES.relax,
380 cmd: PrimitiveCommand::Relax,
381 }),
382 false,
383 )
384 }
385 engine.mouth.requeue(ET::Token::from_cs(name));
386 Ok(())
387}
388
389pub fn endcsname<ET: EngineTypes>(
390 engine: &mut EngineReferences<ET>,
391 _tk: ET::Token,
392) -> TeXResult<(), ET> {
393 engine.general_error("Unexpected `\\endcsname`".to_string())
394}
395
396pub fn count_get<ET: EngineTypes>(
397 engine: &mut EngineReferences<ET>,
398 tk: ET::Token,
399) -> TeXResult<ET::Int, ET> {
400 let idx = engine.read_register_index(false, &tk)?;
401 Ok(engine.state.get_int_register(idx))
402}
403pub fn count_set<ET: EngineTypes>(
404 engine: &mut EngineReferences<ET>,
405 tk: ET::Token,
406 globally: bool,
407) -> TeXResult<(), ET> {
408 let idx = engine.read_register_index(false, &tk)?;
409 let val = engine.read_int(true, &tk)?;
410 engine
411 .state
412 .set_int_register(engine.aux, idx, val, globally);
413 Ok(())
414}
415
416pub fn dimen_get<ET: EngineTypes>(
417 engine: &mut EngineReferences<ET>,
418 tk: ET::Token,
419) -> TeXResult<ET::Dim, ET> {
420 let idx = engine.read_register_index(false, &tk)?;
421 Ok(engine.state.get_dim_register(idx))
422}
423pub fn dimen_set<ET: EngineTypes>(
424 engine: &mut EngineReferences<ET>,
425 tk: ET::Token,
426 globally: bool,
427) -> TeXResult<(), ET> {
428 let idx = engine.read_register_index(false, &tk)?;
429 let val = engine.read_dim(true, &tk)?;
430 engine
431 .state
432 .set_dim_register(engine.aux, idx, val, globally);
433 Ok(())
434}
435
436pub fn skip_get<ET: EngineTypes>(
437 engine: &mut EngineReferences<ET>,
438 tk: ET::Token,
439) -> TeXResult<Skip<ET::Dim>, ET> {
440 let idx = engine.read_register_index(false, &tk)?;
441 Ok(engine.state.get_skip_register(idx))
442}
443pub fn skip_set<ET: EngineTypes>(
444 engine: &mut EngineReferences<ET>,
445 tk: ET::Token,
446 globally: bool,
447) -> TeXResult<(), ET> {
448 let idx = engine.read_register_index(false, &tk)?;
449 let val = engine.read_skip(true, &tk)?;
450 engine
451 .state
452 .set_skip_register(engine.aux, idx, val, globally);
453 Ok(())
454}
455
456pub fn muskip_get<ET: EngineTypes>(
457 engine: &mut EngineReferences<ET>,
458 tk: ET::Token,
459) -> TeXResult<MuSkip<ET::MuDim>, ET> {
460 let idx = engine.read_register_index(false, &tk)?;
461 Ok(engine.state.get_muskip_register(idx))
462}
463pub fn muskip_set<ET: EngineTypes>(
464 engine: &mut EngineReferences<ET>,
465 tk: ET::Token,
466 globally: bool,
467) -> TeXResult<(), ET> {
468 let idx = engine.read_register_index(false, &tk)?;
469 let val = engine.read_muskip(true, &tk)?;
470 engine
471 .state
472 .set_muskip_register(engine.aux, idx, val, globally);
473 Ok(())
474}
475
476pub fn countdef<ET: EngineTypes>(
477 engine: &mut EngineReferences<ET>,
478 tk: ET::Token,
479 globally: bool,
480) -> TeXResult<(), ET> {
481 let name = engine.read_control_sequence(&tk)?;
482 let i = engine.read_register_index(true, &tk)?;
483 let cmd = TeXCommand::IntRegister(i);
484 engine.set_command(&name, Some(cmd), globally);
485 Ok(())
486}
487
488pub fn dimendef<ET: EngineTypes>(
489 engine: &mut EngineReferences<ET>,
490 tk: ET::Token,
491 globally: bool,
492) -> TeXResult<(), ET> {
493 let name = engine.read_control_sequence(&tk)?;
494 let i = engine.read_register_index(true, &tk)?;
495 let cmd = TeXCommand::DimRegister(i);
496 engine.set_command(&name, Some(cmd), globally);
497 Ok(())
498}
499
500pub fn skipdef<ET: EngineTypes>(
501 engine: &mut EngineReferences<ET>,
502 tk: ET::Token,
503 globally: bool,
504) -> TeXResult<(), ET> {
505 let name = engine.read_control_sequence(&tk)?;
506 let i = engine.read_register_index(true, &tk)?;
507 let cmd = TeXCommand::SkipRegister(i);
508 engine.set_command(&name, Some(cmd), globally);
509 Ok(())
510}
511
512pub fn muskipdef<ET: EngineTypes>(
513 engine: &mut EngineReferences<ET>,
514 tk: ET::Token,
515 globally: bool,
516) -> TeXResult<(), ET> {
517 let name = engine.read_control_sequence(&tk)?;
518 let i = engine.read_register_index(true, &tk)?;
519 let cmd = TeXCommand::MuSkipRegister(i);
520 engine.set_command(&name, Some(cmd), globally);
521 Ok(())
522}
523
524pub fn toksdef<ET: EngineTypes>(
525 engine: &mut EngineReferences<ET>,
526 tk: ET::Token,
527 globally: bool,
528) -> TeXResult<(), ET> {
529 let name = engine.read_control_sequence(&tk)?;
530 let i = engine.read_register_index(true, &tk)?;
531 let cmd = TeXCommand::ToksRegister(i);
532 engine.set_command(&name, Some(cmd), globally);
533 Ok(())
534}
535
536pub fn def<ET: EngineTypes>(
537 engine: &mut EngineReferences<ET>,
538 tk: ET::Token,
539 outer: bool,
540 long: bool,
541 protected: bool,
542 globally: bool,
543) -> TeXResult<(), ET> {
544 let cm = loop {
545 let t = engine.need_next(false, &tk)?;
546 match t.to_enum() {
547 StandardToken::Character(c, CommandCode::Active) => break CSOrActiveChar::Active(c),
548 StandardToken::Character(_, CommandCode::Space) => (),
549 StandardToken::ControlSequence(cs) => break CSOrActiveChar::Name(cs),
550 _ => {
551 return engine.general_error("Expected control sequence after `\\def`".to_string())
552 }
553 }
554 };
555 let mut parser = MacroParser::new();
556 engine.iterate(
557 |_, _, t| parser.do_signature_token(t),
558 |_, _, _| {
559 Err(TeXError::General(
560 "Unexpected Token in `\\def`-signature".to_string(),
561 ))
562 },
563 )?;
564 engine.read_until_endgroup(&tk, |_, _, t| parser.do_expansion_token(t))?;
565 let cmd = parser.close(long, outer, protected);
566 engine.set_command(&cm, Some(TeXCommand::Macro(cmd)), globally);
567 Ok(())
568}
569
570pub fn edef<ET: EngineTypes>(
571 engine: &mut EngineReferences<ET>,
572 tk: ET::Token,
573 outer: bool,
574 long: bool,
575 protected: bool,
576 globally: bool,
577) -> TeXResult<(), ET> {
578 let cm = loop {
579 let t = engine.need_next(false, &tk)?;
580 match t.to_enum() {
581 StandardToken::Character(c, CommandCode::Active) => break CSOrActiveChar::Active(c),
582 StandardToken::Character(_, CommandCode::Space) => (),
583 StandardToken::ControlSequence(cs) => break CSOrActiveChar::Name(cs),
584 _ => {
585 return engine.general_error("Expected control sequence after `\\edef`".to_string())
586 }
587 }
588 };
589
590 let mut parser = MacroParser::new();
591 engine.iterate(
592 |_, _, t| parser.do_signature_token(t),
593 |_, _, _| {
594 Err(TeXError::General(
595 "Unexpected token in `\\edef`-signature".to_string(),
596 ))
597 },
598 )?;
599 engine.expand_until_endgroup(false, true, &tk, |_, _, t| parser.do_expansion_token(t))?;
600 let cmd = parser.close(long, outer, protected);
601 engine.set_command(&cm, Some(TeXCommand::Macro(cmd)), globally);
602 Ok(())
603}
604
605pub fn xdef<ET: EngineTypes>(
606 engine: &mut EngineReferences<ET>,
607 tk: ET::Token,
608 outer: bool,
609 long: bool,
610 protected: bool,
611 _globally: bool,
612) -> TeXResult<(), ET> {
613 edef(engine, tk, outer, long, protected, true)
614}
615
616pub fn gdef<ET: EngineTypes>(
617 engine: &mut EngineReferences<ET>,
618 tk: ET::Token,
619 outer: bool,
620 long: bool,
621 protected: bool,
622 _globally: bool,
623) -> TeXResult<(), ET> {
624 def(engine, tk, outer, long, protected, true)
625}
626
627pub fn dp_get<ET: EngineTypes>(
628 engine: &mut EngineReferences<ET>,
629 tk: ET::Token,
630) -> TeXResult<ET::Dim, ET> {
631 let idx = engine.read_register_index(false, &tk)?;
632 Ok(match engine.state.get_box_register(idx) {
633 None => ET::Dim::default(),
634 Some(b) => b.depth(),
635 })
636}
637pub fn dp_set<ET: EngineTypes>(
638 engine: &mut EngineReferences<ET>,
639 tk: ET::Token,
640 _globally: bool,
641) -> TeXResult<(), ET> {
642 let idx = engine.read_register_index(false, &tk)?;
643 let dim = engine.read_dim(true, &tk)?;
644 if let Some(b) = engine.state.get_box_register_mut(idx) {
645 b.assign_depth(dim)
646 }
647 Ok(())
648}
649
650pub fn ht_get<ET: EngineTypes>(
651 engine: &mut EngineReferences<ET>,
652 tk: ET::Token,
653) -> TeXResult<ET::Dim, ET> {
654 let idx = engine.read_register_index(false, &tk)?;
655 Ok(match engine.state.get_box_register(idx) {
656 None => ET::Dim::default(),
657 Some(b) => b.height(),
658 })
659}
660pub fn ht_set<ET: EngineTypes>(
661 engine: &mut EngineReferences<ET>,
662 tk: ET::Token,
663 _globally: bool,
664) -> TeXResult<(), ET> {
665 let idx = engine.read_register_index(false, &tk)?;
666 let dim = engine.read_dim(true, &tk)?;
667 if let Some(b) = engine.state.get_box_register_mut(idx) {
668 b.assign_height(dim)
669 }
670 Ok(())
671}
672
673pub fn wd_get<ET: EngineTypes>(
674 engine: &mut EngineReferences<ET>,
675 tk: ET::Token,
676) -> TeXResult<ET::Dim, ET> {
677 let idx = engine.read_register_index(false, &tk)?;
678 Ok(match engine.state.get_box_register(idx) {
679 None => ET::Dim::default(),
680 Some(b) => b.width(),
681 })
682}
683pub fn wd_set<ET: EngineTypes>(
684 engine: &mut EngineReferences<ET>,
685 tk: ET::Token,
686 _globally: bool,
687) -> TeXResult<(), ET> {
688 let idx = engine.read_register_index(false, &tk)?;
689 let dim = engine.read_dim(true, &tk)?;
690 if let Some(b) = engine.state.get_box_register_mut(idx) {
691 b.assign_width(dim)
692 }
693 Ok(())
694}
695
696pub fn global<ET: EngineTypes>(
697 engine: &mut EngineReferences<ET>,
698 tk: ET::Token,
699 outer: bool,
700 long: bool,
701 protected: bool,
702 _globally: bool,
703) -> TeXResult<(), ET> {
704 let allow_others = !outer && !long && !protected;
705 crate::expand_loop!(engine,token,
706 ResolvedToken::Cmd(Some(TeXCommand::Primitive{name,cmd:PrimitiveCommand::Assignment(a)})) => match *name {
707 n if n == PRIMITIVES.outer => return self::outer(engine,token,outer,long,protected,true),
708 n if n == PRIMITIVES.long => return self::long(engine,token,outer,long,protected,true),
709 n if n == PRIMITIVES.protected => return super::etex::protected(engine,token,outer,long,protected,true),
710 n if n == PRIMITIVES.global => return self::global(engine,token,outer,long,protected,true),
711 n if n == PRIMITIVES.def => return self::def(engine,token,outer,long,protected,true),
712 n if n == PRIMITIVES.edef => return self::edef(engine,token,outer,long,protected,true),
713 n if n == PRIMITIVES.xdef => return self::xdef(engine,token,outer,long,protected,true),
714 n if n == PRIMITIVES.gdef => return self::gdef(engine,token,outer,long,protected,true),
715 n if allow_others => return ET::Stomach::do_assignment(engine,n,token,*a,true),
716 _ => return engine.general_error(format!("Illegal command after `\\global`: {}",name.display::<ET::Char>(Some(b'\\'.into()))))
717 }
718 ResolvedToken::Cmd(Some(TeXCommand::IntRegister(u))) if allow_others =>
719 return ET::Stomach::assign_int_register(engine,*u,true,tk),
720 ResolvedToken::Cmd(Some(TeXCommand::Primitive{name,cmd:PrimitiveCommand::PrimitiveInt})) if allow_others =>
721 return ET::Stomach::assign_primitive_int(engine,*name,true,tk),
722 ResolvedToken::Cmd(Some(TeXCommand::Primitive{cmd:PrimitiveCommand::Int {assign:Some(f),..},name})) if allow_others =>
723 return ET::Stomach::do_assignment(engine,*name,token,*f,true),
724 ResolvedToken::Cmd(Some(TeXCommand::DimRegister(u))) if allow_others =>
725 return ET::Stomach::assign_dim_register(engine,*u,true,tk),
726 ResolvedToken::Cmd(Some(TeXCommand::Primitive{name,cmd:PrimitiveCommand::PrimitiveDim})) if allow_others =>
727 return ET::Stomach::assign_primitive_dim(engine,*name,true,tk),
728 ResolvedToken::Cmd(Some(TeXCommand::Primitive{cmd:PrimitiveCommand::Dim {assign:Some(f),..},name})) if allow_others =>
729 return ET::Stomach::do_assignment(engine,*name,token,*f,true),
730 ResolvedToken::Cmd(Some(TeXCommand::SkipRegister(u))) if allow_others =>
731 return ET::Stomach::assign_skip_register(engine,*u,true,tk),
732 ResolvedToken::Cmd(Some(TeXCommand::Primitive{name,cmd:PrimitiveCommand::PrimitiveSkip})) if allow_others =>
733 return ET::Stomach::assign_primitive_skip(engine,*name,true,tk),
734 ResolvedToken::Cmd(Some(TeXCommand::Primitive{cmd:PrimitiveCommand::Skip {assign:Some(f),..},name})) if allow_others =>
735 return ET::Stomach::do_assignment(engine,*name,token,*f,true),
736 ResolvedToken::Cmd(Some(TeXCommand::MuSkipRegister(u))) if allow_others =>
737 return ET::Stomach::assign_muskip_register(engine,*u,true,tk),
738 ResolvedToken::Cmd(Some(TeXCommand::Primitive{name,cmd:PrimitiveCommand::PrimitiveMuSkip})) if allow_others =>
739 return ET::Stomach::assign_primitive_muskip(engine,*name,true,tk),
740 ResolvedToken::Cmd(Some(TeXCommand::Primitive{cmd:PrimitiveCommand::MuSkip {assign:Some(f),..},name})) if allow_others =>
741 return ET::Stomach::do_assignment(engine,*name,token,*f,true),
742 ResolvedToken::Cmd(Some(TeXCommand::ToksRegister(u))) if allow_others =>
743 return ET::Stomach::assign_toks_register(engine,token,*u,true),
744 ResolvedToken::Cmd(Some(TeXCommand::Primitive{name,cmd:PrimitiveCommand::PrimitiveToks})) if allow_others =>
745 return ET::Stomach::assign_primitive_toks(engine,token,*name,true),
746 ResolvedToken::Cmd(Some(TeXCommand::Primitive{cmd:PrimitiveCommand::FontCmd {assign:Some(f),..},name})) if allow_others =>
747 return ET::Stomach::do_assignment(engine,*name,token,*f,true),
748 ResolvedToken::Cmd(Some(TeXCommand::Primitive{cmd:PrimitiveCommand::Whatsit {get,..},name})) if allow_others =>
749 return ET::Stomach::do_whatsit(engine,*name,token,*get),
750 ResolvedToken::Cmd(Some(TeXCommand::Primitive {cmd:PrimitiveCommand::Relax,..})) => (),
751 ResolvedToken::Cmd(Some(_)) => {
752 let s = token.display(engine.aux.memory.cs_interner(),engine.state.get_catcode_scheme(),engine.state.get_escape_char()).to_string();
753 return engine.general_error(format!("Illegal command after `\\global`: {s}"))
754 }
755 _ => {
756 let s = token.display(engine.aux.memory.cs_interner(),engine.state.get_catcode_scheme(),engine.state.get_escape_char()).to_string();
757 return engine.general_error(format!("Illegal command after `\\global`: {s}"))
758 }
759 );
760 TeXError::file_end_while_use(engine.aux, engine.state, engine.mouth, &tk)
761}
762pub fn outer<ET: EngineTypes>(
763 engine: &mut EngineReferences<ET>,
764 tk: ET::Token,
765 _outer: bool,
766 long: bool,
767 protected: bool,
768 globally: bool,
769) -> TeXResult<(), ET> {
770 crate::expand_loop!(engine,token,
771 ResolvedToken::Cmd(Some(TeXCommand::Primitive{name,..})) => match *name {
772 n if n == PRIMITIVES.outer => return self::outer(engine,token,true,long,protected,globally),
773 n if n == PRIMITIVES.long => return self::long(engine,token,true,long,protected,globally),
774 n if n == PRIMITIVES.protected => return super::etex::protected(engine,token,true,long,protected,globally),
775 n if n == PRIMITIVES.global => return self::global(engine,token,true,long,protected,globally),
776 n if n == PRIMITIVES.def => return self::def(engine,token,true,long,protected,globally),
777 n if n == PRIMITIVES.edef => return self::edef(engine,token,true,long,protected,globally),
778 n if n == PRIMITIVES.xdef => return self::xdef(engine,token,true,long,protected,globally),
779 n if n == PRIMITIVES.gdef => return self::gdef(engine,token,true,long,protected,globally),
780 n if n == PRIMITIVES.relax => (),
781 n => {
782 let s = n.display(engine.state.get_escape_char()).to_string();
783 engine.requeue(token)?;
784 return Err(TeXError::General(format!("You can't use a prefix with '{}'",s)))
785 }
786 }
787 _ => {
788 let s = token.display(engine.aux.memory.cs_interner(),engine.state.get_catcode_scheme(),engine.state.get_escape_char()).to_string();
789 engine.requeue(token)?;
790 return Err(TeXError::General(format!("You can't use a prefix with '{}'",s)))
791 }
792 );
793 TeXError::file_end_while_use(engine.aux, engine.state, engine.mouth, &tk)
794}
795
796pub fn long<ET: EngineTypes>(
797 engine: &mut EngineReferences<ET>,
798 tk: ET::Token,
799 outer: bool,
800 _long: bool,
801 protected: bool,
802 globally: bool,
803) -> TeXResult<(), ET> {
804 crate::expand_loop!(engine,token,
805 ResolvedToken::Cmd(Some(TeXCommand::Primitive{name,..})) => match *name {
806 n if n == PRIMITIVES.outer => return self::outer(engine,token,outer,true,protected,globally),
807 n if n == PRIMITIVES.long => return self::long(engine,token,outer,true,protected,globally),
808 n if n == PRIMITIVES.protected => return super::etex::protected(engine,token,outer,true,protected,globally),
809 n if n == PRIMITIVES.global => return self::global(engine,token,outer,true,protected,globally),
810 n if n == PRIMITIVES.def => return self::def(engine,token,outer,true,protected,globally),
811 n if n == PRIMITIVES.edef => return self::edef(engine,token,outer,true,protected,globally),
812 n if n == PRIMITIVES.xdef => return self::xdef(engine,token,outer,true,protected,globally),
813 n if n == PRIMITIVES.gdef => return self::gdef(engine,token,outer,true,protected,globally),
814 n if n == PRIMITIVES.relax => (),
815 n => {
816 let s = n.display(engine.state.get_escape_char()).to_string();
817 engine.requeue(token)?;
818 return Err(TeXError::General(format!("You can't use a prefix with '{}'",s)))
819 }
820 }
821 _ => {
822 let s = token.display(engine.aux.memory.cs_interner(),engine.state.get_catcode_scheme(),engine.state.get_escape_char()).to_string();
823 engine.requeue(token)?;
824 return Err(TeXError::General(format!("You can't use a prefix with '{}'",s)))
825 }
826 );
827 TeXError::file_end_while_use(engine.aux, engine.state, engine.mouth, &tk)
828}
829
830macro_rules! modify_num {
831 ($engine:ident,$globally:ident,$int:expr,$dim:expr,$skip:expr) => {
832 crate::expand_loop!($engine,token,
833 ResolvedToken::Cmd(Some(cm)) => match cm {
834 TeXCommand::Primitive{name,..} if *name == PRIMITIVES.count => {
835 let idx = $engine.read_register_index(false,&token)?;
836 return crate::commands::methods::modify_int_register($engine,idx,$globally,$int)
837 }
838 TeXCommand::IntRegister(idx) => {
839 return crate::commands::methods::modify_int_register($engine,*idx,$globally,$int)
840 }
841 TeXCommand::Primitive{name,cmd:PrimitiveCommand::PrimitiveInt} => {
842 return crate::commands::methods::modify_primitive_int($engine,*name,$globally,$int)
843 }
844 TeXCommand::Primitive{name,..} if *name == PRIMITIVES.dimen => {
845 let idx = $engine.read_register_index(false,&token)?;
846 return crate::commands::methods::modify_dim_register($engine,idx,$globally,$dim)
847 }
848 TeXCommand::DimRegister(idx) => {
849 return crate::commands::methods::modify_dim_register($engine,*idx,$globally,$dim)
850 }
851 TeXCommand::Primitive{name,cmd:PrimitiveCommand::PrimitiveDim} => {
852 return crate::commands::methods::modify_primitive_dim($engine,*name,$globally,$dim)
853 }
854 TeXCommand::Primitive{name,..} if *name == PRIMITIVES.skip => {
855 let idx = $engine.read_register_index(false,&token)?;
856 return crate::commands::methods::modify_skip_register($engine,idx,$globally,$skip)
857 }
858 TeXCommand::SkipRegister(idx) => {
859 return crate::commands::methods::modify_skip_register($engine,*idx,$globally,$skip)
860 }
861 TeXCommand::Primitive{name,cmd:PrimitiveCommand::PrimitiveSkip} => {
862 return crate::commands::methods::modify_primitive_skip($engine,*name,$globally,$skip)
863 }
864 _ => {
865 let s = token.display($engine.aux.memory.cs_interner(),$engine.state.get_catcode_scheme(),$engine.state.get_escape_char()).to_string();
866 $engine.requeue(token)?;
867 return Err(TeXError::General(format!("Unexpected token in \\divide/\\multiply: {s}")))
868 }
869 }
870 _ => {
871 let s = token.display($engine.aux.memory.cs_interner(),$engine.state.get_catcode_scheme(),$engine.state.get_escape_char()).to_string();
872 $engine.requeue(token)?;
873 return Err(TeXError::General(format!("Unexpected token in \\divide/\\multiply: {s}")))
874 }
875 );
876 };
877}
878
879pub fn advance<ET: EngineTypes>(
880 engine: &mut EngineReferences<ET>,
881 tk: ET::Token,
882 globally: bool,
883) -> TeXResult<(), ET> {
884 modify_num!(
885 engine,
886 globally,
887 |a, e| Ok(a + e.read_int(false, &tk)?),
888 |a, e| Ok(a + e.read_dim(false, &tk)?),
889 |a, e| Ok(a + e.read_skip(false, &tk)?)
890 );
891 TeXError::file_end_while_use(engine.aux, engine.state, engine.mouth, &tk)
892}
893pub fn divide<ET: EngineTypes>(
894 engine: &mut EngineReferences<ET>,
895 tk: ET::Token,
896 globally: bool,
897) -> TeXResult<(), ET> {
898 modify_num!(
899 engine,
900 globally,
901 |a, e| {
902 let b = e.read_int(false, &tk)?;
903 if b == ET::Int::default() {
904 return Err(TeXError::General("Division by zero".to_string()));
905 }
906 Ok(a / b)
907 },
908 |a, e| {
909 let b = e.read_int(false, &tk)?;
910 if b == ET::Int::default() {
911 return Err(TeXError::General("Division by zero".to_string()));
912 }
913 Ok(a / b)
914 },
915 |a, e| {
916 let b = e.read_int(false, &tk)?;
917 if b == ET::Int::default() {
918 return Err(TeXError::General("Division by zero".to_string()));
919 }
920 Ok(a / b)
921 }
922 );
923 TeXError::file_end_while_use(engine.aux, engine.state, engine.mouth, &tk)
924}
925pub fn multiply<ET: EngineTypes>(
926 engine: &mut EngineReferences<ET>,
927 tk: ET::Token,
928 globally: bool,
929) -> TeXResult<(), ET> {
930 modify_num!(
931 engine,
932 globally,
933 |a, e| Ok(a * e.read_int(false, &tk)?),
934 |a, e| {
935 let b = e.read_int(false, &tk)?;
936 Ok(a * b)
937 },
938 |a, e| {
939 let b = e.read_int(false, &tk)?;
940 Ok(a * b)
941 }
942 );
943 TeXError::file_end_while_use(engine.aux, engine.state, engine.mouth, &tk)
944}
945
946pub fn r#else<ET: EngineTypes>(
947 engine: &mut EngineReferences<ET>,
948 tk: ET::Token,
949) -> TeXResult<(), ET> {
950 let conds = engine.gullet.get_conditionals();
951 let name = match conds.pop() {
952 Some(ActiveConditional::True(id)) => {
953 conds.push(ActiveConditional::Else(id));
954 id
955 }
956 Some(c @ ActiveConditional::Case(_)) => {
957 conds.push(c);
958 PRIMITIVES.ifcase
959 }
960 Some(u @ ActiveConditional::Unfinished(_)) => {
961 conds.push(u);
962 engine.mouth.requeue(tk);
963 let relax = engine.aux.memory.cs_interner_mut().cs_from_str("relax");
964 engine.mouth.requeue(ET::Token::from_cs(relax));
965 return Ok(());
966 }
967 Some(ActiveConditional::Else(_)) => {
968 return Err(TeXError::General(
969 "Unexpected `\\else` in `\\else`-branch".to_string(),
970 ))
971 }
972 _ => {
973 return Err(TeXError::General(
974 "Unexpected `\\else` outside of a condition".to_string(),
975 ))
976 }
977 };
978 let trace = engine.state.get_primitive_int(PRIMITIVES.tracingifs) > ET::Int::default();
979 let index = conds.len();
980 if trace {
981 engine.aux.outputs.write_neg1(format_args!(
982 "{{{}else: {} (level {}) entered on line {}}}",
983 <ET::Char as Character>::display_opt(engine.state.get_escape_char()),
984 name.display(engine.state.get_escape_char()),
985 index,
986 engine.mouth.line_number()
987 ));
988 }
989 crate::engine::gullet::methods::false_loop(engine, index, false, false)?;
990 if trace {
991 engine.aux.outputs.write_neg1(format_args!(
992 "{{{}fi: {} (level {}) entered on line {}}}",
993 <ET::Char as Character>::display_opt(engine.state.get_escape_char()),
994 name.display(engine.state.get_escape_char()),
995 index,
996 engine.mouth.line_number()
997 ));
998 }
999 Ok(())
1000}
1001
1002pub fn or<ET: EngineTypes>(engine: &mut EngineReferences<ET>, tk: ET::Token) -> TeXResult<(), ET> {
1003 let conds = engine.gullet.get_conditionals();
1004 match conds.pop() {
1005 Some(ActiveConditional::Case(_)) => {
1006 conds.push(ActiveConditional::Else(PRIMITIVES.ifcase));
1007 }
1008 Some(u @ ActiveConditional::Unfinished(_)) => {
1009 conds.push(u);
1010 engine.mouth.requeue(tk);
1011 let relax = engine.aux.memory.cs_interner_mut().cs_from_str("relax");
1012 engine.mouth.requeue(ET::Token::from_cs(relax));
1013 return Ok(());
1014 }
1015 _ => return engine.general_error("`\\or` outside of `\\case`".to_string()),
1016 };
1017 let trace = engine.state.get_primitive_int(PRIMITIVES.tracingifs) > ET::Int::default();
1018 let index = conds.len();
1019 if trace {
1020 engine.aux.outputs.write_neg1(format_args!(
1021 "{{{}or: {}ifcase (level {}) entered on line {}}}",
1022 <ET::Char as Character>::display_opt(engine.state.get_escape_char()),
1023 <ET::Char as Character>::display_opt(engine.state.get_escape_char()),
1024 index,
1025 engine.mouth.line_number()
1026 ));
1027 }
1028 crate::engine::gullet::methods::false_loop(engine, index, false, true)?;
1029 if trace {
1030 engine.aux.outputs.write_neg1(format_args!(
1031 "{{{}fi: {}ifcase (level {}) entered on line {}}}",
1032 <ET::Char as Character>::display_opt(engine.state.get_escape_char()),
1033 <ET::Char as Character>::display_opt(engine.state.get_escape_char()),
1034 index,
1035 engine.mouth.line_number()
1036 ));
1037 }
1038 Ok(())
1039}
1040
1041pub fn endlinechar_get<ET: EngineTypes>(
1042 engine: &mut EngineReferences<ET>,
1043 _tk: ET::Token,
1044) -> TeXResult<ET::Int, ET> {
1045 Ok(ET::Int::from(match engine.state.get_endline_char() {
1046 Some(c) => c.into() as i32,
1047 _ => -1,
1048 }))
1049}
1050pub fn endlinechar_set<ET: EngineTypes>(
1051 engine: &mut EngineReferences<ET>,
1052 tk: ET::Token,
1053 globally: bool,
1054) -> TeXResult<(), ET> {
1055 let val: i64 = engine.read_int(true, &tk)?.into();
1056 let val = match val.cmp(&-1) {
1057 Ordering::Less => return engine.general_error(format!("Illegal endline character: {val}")),
1058 Ordering::Equal => None,
1059 Ordering::Greater => match ET::Char::try_from(val as u64) {
1060 Ok(c) => Some(c),
1061 _ => return engine.general_error(format!("Illegal endline character: {val}")),
1062 },
1063 };
1064 engine.state.set_endline_char(engine.aux, val, globally);
1065 Ok(())
1066}
1067
1068pub fn escapechar_get<ET: EngineTypes>(
1069 engine: &mut EngineReferences<ET>,
1070 _tk: ET::Token,
1071) -> TeXResult<ET::Int, ET> {
1072 Ok(ET::Int::from(match engine.state.get_escape_char() {
1073 Some(c) => c.into() as i32,
1074 _ => -1,
1075 }))
1076}
1077pub fn escapechar_set<ET: EngineTypes>(
1078 engine: &mut EngineReferences<ET>,
1079 tk: ET::Token,
1080 globally: bool,
1081) -> TeXResult<(), ET> {
1082 let val: i64 = engine.read_int(true, &tk)?.into();
1083 let val = match val.cmp(&-1) {
1084 Ordering::Less => return engine.general_error(format!("Illegal escape character: {val}")),
1085 Ordering::Equal => None,
1086 Ordering::Greater => match ET::Char::try_from(val as u64) {
1087 Ok(c) => Some(c),
1088 _ => return engine.general_error(format!("Illegal escape character: {val}")),
1089 },
1090 };
1091 engine.state.set_escape_char(engine.aux, val, globally);
1092 Ok(())
1093}
1094
1095pub fn font_get<ET: EngineTypes>(
1096 engine: &mut EngineReferences<ET>,
1097 _tk: ET::Token,
1098) -> TeXResult<ET::Font, ET> {
1099 Ok(engine.state.get_current_font().clone())
1100}
1101
1102pub fn font_set<ET: EngineTypes>(
1103 engine: &mut EngineReferences<ET>,
1104 tk: ET::Token,
1105 global: bool,
1106) -> TeXResult<(), ET> {
1107 let cs = match engine.read_control_sequence(&tk)? {
1108 CSOrActiveChar::Name(name) => name,
1109 _ => return engine.general_error("control sequence expected after \\font".to_string()),
1110 };
1111 let mut name = engine.aux.memory.get_string();
1112 engine.read_string(true, &mut name, &tk)?;
1113 let mut font = engine
1114 .fontsystem
1115 .new_font(&name, cs.clone(), engine.filesystem);
1116 engine.aux.memory.return_string(name);
1117 match engine.read_keywords(&[b"at", b"scaled"])? {
1118 Some(b"at") => {
1119 let size = engine.read_dim(false, &tk)?;
1120 font.set_at(size);
1121 }
1122 Some(b"scaled") => {
1123 let i = engine.read_int(false, &tk)?.into();
1124 let at = font.get_at();
1125 font.set_at(at.scale_float(i as f64 / 1000.0));
1126 }
1127 _ => (),
1128 }
1129 engine
1130 .state
1131 .set_command(engine.aux, cs, Some(TeXCommand::Font(font)), global);
1132 Ok(())
1133}
1134
1135pub fn textfont_get<ET: EngineTypes>(
1136 engine: &mut EngineReferences<ET>,
1137 tk: ET::Token,
1138) -> TeXResult<ET::Font, ET> {
1139 let num = engine.mathfont_index(false, &tk)?;
1140 Ok(engine.state.get_textfont(num).clone())
1141}
1142
1143pub fn textfont_set<ET: EngineTypes>(
1144 engine: &mut EngineReferences<ET>,
1145 tk: ET::Token,
1146 global: bool,
1147) -> TeXResult<(), ET> {
1148 let num = engine.mathfont_index(false, &tk)?;
1149 let fnt = engine.read_font(true, &tk)?;
1150 engine.state.set_textfont(engine.aux, num, fnt, global);
1151 Ok(())
1152}
1153
1154pub fn scriptfont_get<ET: EngineTypes>(
1155 engine: &mut EngineReferences<ET>,
1156 tk: ET::Token,
1157) -> TeXResult<ET::Font, ET> {
1158 let num = engine.mathfont_index(false, &tk)?;
1159 Ok(engine.state.get_scriptfont(num).clone())
1160}
1161
1162pub fn scriptfont_set<ET: EngineTypes>(
1163 engine: &mut EngineReferences<ET>,
1164 tk: ET::Token,
1165 global: bool,
1166) -> TeXResult<(), ET> {
1167 let num = engine.mathfont_index(false, &tk)?;
1168 let fnt = engine.read_font(true, &tk)?;
1169 engine.state.set_scriptfont(engine.aux, num, fnt, global);
1170 Ok(())
1171}
1172
1173pub fn scriptscriptfont_get<ET: EngineTypes>(
1174 engine: &mut EngineReferences<ET>,
1175 tk: ET::Token,
1176) -> TeXResult<ET::Font, ET> {
1177 let num = engine.mathfont_index(false, &tk)?;
1178 Ok(engine.state.get_scriptscriptfont(num).clone())
1179}
1180
1181pub fn scriptscriptfont_set<ET: EngineTypes>(
1182 engine: &mut EngineReferences<ET>,
1183 tk: ET::Token,
1184 global: bool,
1185) -> TeXResult<(), ET> {
1186 let num = engine.mathfont_index(false, &tk)?;
1187 let fnt = engine.read_font(true, &tk)?;
1188 engine
1189 .state
1190 .set_scriptscriptfont(engine.aux, num, fnt, global);
1191 Ok(())
1192}
1193
1194pub fn fontdimen_get<ET: EngineTypes>(
1195 engine: &mut EngineReferences<ET>,
1196 tk: ET::Token,
1197) -> TeXResult<ET::Dim, ET> {
1198 let idx = match engine.read_int(false, &tk)?.try_into() {
1199 Ok(i) if i > 0 && i - 1 <= u16::MAX.into() => (i - 1) as u16,
1200 _ => {
1201 engine.general_error("Illegal \\fontdimen index".to_string())?;
1202 return Ok(ET::Dim::default());
1203 }
1204 };
1205 let font = engine.read_font(false, &tk)?;
1206 Ok(font.get_dim(idx))
1207}
1208pub fn fontdimen_set<ET: EngineTypes>(
1209 engine: &mut EngineReferences<ET>,
1210 tk: ET::Token,
1211 _globally: bool,
1212) -> TeXResult<(), ET> {
1213 let i = engine.read_int(false, &tk)?;
1214 let idx = match i.try_into() {
1215 Ok(i) if i > 0 && i - 1 <= u16::MAX.into() => (i - 1) as u16,
1216 _ => return engine.general_error("Illegal \\fontdimen index".to_string()),
1217 };
1218 let mut font = engine.read_font(false, &tk)?;
1219 let dim = engine.read_dim(true, &tk)?;
1220 font.set_dim(idx, dim);
1221 Ok(())
1222}
1223
1224pub fn box_<ET: EngineTypes>(
1225 engine: &mut EngineReferences<ET>,
1226 tk: ET::Token,
1227) -> TeXResult<Either<Option<TeXBox<ET>>, BoxInfo<ET>>, ET> {
1228 let idx = engine.read_register_index(false, &tk)?;
1229 Ok(either::Left(engine.state.take_box_register(idx)))
1230}
1231pub fn copy<ET: EngineTypes>(
1232 engine: &mut EngineReferences<ET>,
1233 tk: ET::Token,
1234) -> TeXResult<Either<Option<TeXBox<ET>>, BoxInfo<ET>>, ET> {
1235 let idx = engine.read_register_index(false, &tk)?;
1236 Ok(either::Left(engine.state.get_box_register(idx).cloned()))
1237}
1238
1239pub fn unbox<ET: EngineTypes>(
1240 engine: &mut EngineReferences<ET>,
1241 tk: ET::Token,
1242 tp: BoxType,
1243 copy: bool,
1244) -> TeXResult<(), ET> {
1245 let idx = engine.read_register_index(false, &tk)?;
1246 let bx = if copy {
1247 engine.state.get_box_register(idx).cloned()
1248 } else {
1249 engine.state.take_box_register(idx)
1250 };
1251 match bx {
1252 None => (),
1253 Some(TeXBox::V { children, .. }) if tp == BoxType::Vertical => {
1254 for c in children.into_vec() {
1255 ET::Stomach::add_node_v(engine, c)?
1256 }
1257 }
1258 Some(TeXBox::H { children, .. }) if tp == BoxType::Horizontal => {
1259 match engine.stomach.data_mut().open_lists.last_mut() {
1260 Some(NodeList::Horizontal { children: ls, .. }) => ls.extend(children.into_vec()),
1261 _ => {
1262 return engine.general_error(
1263 "Cannot unbox \\hbox outside of horizontal mode".to_string(),
1264 )
1265 }
1266 }
1267 }
1268 _ => return engine.general_error(format!("Cannot unbox box {idx} in {tp} mode")),
1269 }
1270 Ok(())
1271}
1272
1273pub fn unhbox<ET: EngineTypes>(
1274 engine: &mut EngineReferences<ET>,
1275 tk: ET::Token,
1276) -> TeXResult<(), ET> {
1277 unbox(engine, tk, BoxType::Horizontal, false)
1278}
1279pub fn unhcopy<ET: EngineTypes>(
1280 engine: &mut EngineReferences<ET>,
1281 tk: ET::Token,
1282) -> TeXResult<(), ET> {
1283 unbox(engine, tk, BoxType::Horizontal, true)
1284}
1285pub fn unvbox<ET: EngineTypes>(
1286 engine: &mut EngineReferences<ET>,
1287 tk: ET::Token,
1288) -> TeXResult<(), ET> {
1289 unbox(engine, tk, BoxType::Vertical, false)
1290}
1291pub fn unvcopy<ET: EngineTypes>(
1292 engine: &mut EngineReferences<ET>,
1293 tk: ET::Token,
1294) -> TeXResult<(), ET> {
1295 unbox(engine, tk, BoxType::Vertical, true)
1296}
1297
1298pub fn hbox<ET: EngineTypes>(
1299 engine: &mut EngineReferences<ET>,
1300 tk: ET::Token,
1301) -> TeXResult<Either<Option<TeXBox<ET>>, BoxInfo<ET>>, ET> {
1302 let scaled = super::methods::do_box_start(engine, GroupType::HBox, PRIMITIVES.everyhbox, &tk)?;
1303 Ok(either::Right(BoxInfo::H(HBoxInfo::new_box(scaled))))
1304}
1305
1306pub fn vbox<ET: EngineTypes>(
1307 engine: &mut EngineReferences<ET>,
1308 tk: ET::Token,
1309) -> TeXResult<Either<Option<TeXBox<ET>>, BoxInfo<ET>>, ET> {
1310 let scaled = super::methods::do_box_start(engine, GroupType::VBox, PRIMITIVES.everyvbox, &tk)?;
1311 Ok(either::Right(BoxInfo::V(VBoxInfo::new_box(scaled))))
1312}
1313
1314pub fn vtop<ET: EngineTypes>(
1315 engine: &mut EngineReferences<ET>,
1316 tk: ET::Token,
1317) -> TeXResult<Either<Option<TeXBox<ET>>, BoxInfo<ET>>, ET> {
1318 let scaled = super::methods::do_box_start(engine, GroupType::VTop, PRIMITIVES.everyvbox, &tk)?;
1319 Ok(either::Right(BoxInfo::V(VBoxInfo::new_top(scaled))))
1320}
1321
1322pub fn vcenter<ET: EngineTypes>(
1323 engine: &mut EngineReferences<ET>,
1324 tk: ET::Token,
1325) -> TeXResult<(), ET> {
1326 let scaled =
1327 super::methods::do_box_start(engine, GroupType::VCenter, PRIMITIVES.everyvbox, &tk)?;
1328 engine
1329 .stomach
1330 .data_mut()
1331 .open_lists
1332 .push(NodeList::Vertical {
1333 children: Vec::new(),
1334 tp: VerticalNodeListType::VCenter(engine.mouth.start_ref(), scaled),
1335 });
1336 Ok(())
1337}
1338
1339pub fn halign<ET: EngineTypes>(
1340 engine: &mut EngineReferences<ET>,
1341 tk: ET::Token,
1342) -> TeXResult<(), ET> {
1343 match engine.stomach.data_mut().mode() {
1344 TeXMode::Horizontal => {
1345 engine.requeue(tk)?;
1346 return ET::Stomach::close_paragraph(engine);
1347 }
1348 TeXMode::DisplayMath | TeXMode::Vertical | TeXMode::InternalVertical => (),
1349 _ => return engine.general_error("\\halign not allowed in math mode".to_string()),
1350 }
1351 let wd = if engine.read_keyword(b"to")? {
1352 Some(engine.read_dim(false, &tk)?)
1353 } else {
1354 None
1355 };
1356 super::methods::do_align(engine, BoxType::Horizontal, BoxType::Vertical, wd, &tk)
1357}
1358
1359pub fn valign<ET: EngineTypes>(
1360 engine: &mut EngineReferences<ET>,
1361 tk: ET::Token,
1362) -> TeXResult<(), ET> {
1363 let wd = if engine.read_keyword(b"to")? {
1364 Some(engine.read_dim(false, &tk)?)
1365 } else {
1366 None
1367 };
1368 super::methods::do_align(engine, BoxType::Vertical, BoxType::Horizontal, wd, &tk)
1369}
1370
1371pub fn hyphenchar_get<ET: EngineTypes>(
1372 engine: &mut EngineReferences<ET>,
1373 tk: ET::Token,
1374) -> TeXResult<ET::Int, ET> {
1375 let font = engine.read_font(false, &tk)?;
1376 Ok(font.get_hyphenchar())
1377}
1378pub fn hyphenchar_set<ET: EngineTypes>(
1379 engine: &mut EngineReferences<ET>,
1380 tk: ET::Token,
1381 _globally: bool,
1382) -> TeXResult<(), ET> {
1383 let mut font = engine.read_font(false, &tk)?;
1384 let val = engine.read_int(true, &tk)?;
1385 font.set_hyphenchar(val);
1386 Ok(())
1387}
1388
1389pub fn skewchar_get<ET: EngineTypes>(
1390 engine: &mut EngineReferences<ET>,
1391 tk: ET::Token,
1392) -> TeXResult<ET::Int, ET> {
1393 let font = engine.read_font(false, &tk)?;
1394 Ok(font.get_skewchar())
1395}
1396pub fn skewchar_set<ET: EngineTypes>(
1397 engine: &mut EngineReferences<ET>,
1398 tk: ET::Token,
1399 _globally: bool,
1400) -> TeXResult<(), ET> {
1401 let mut font = engine.read_font(false, &tk)?;
1402 let val = engine.read_int(true, &tk)?;
1403 font.set_skewchar(val);
1404 Ok(())
1405}
1406
1407pub fn r#if<ET: EngineTypes>(
1408 engine: &mut EngineReferences<ET>,
1409 tk: ET::Token,
1410) -> TeXResult<bool, ET> {
1411 let first = super::methods::get_if_token(engine, &tk)?;
1412 let second = super::methods::get_if_token(engine, &tk)?;
1413 Ok(first.0 == second.0)
1414}
1415
1416pub fn ifcase<ET: EngineTypes>(
1417 engine: &mut EngineReferences<ET>,
1418 tk: ET::Token,
1419) -> TeXResult<bool, ET> {
1420 let index = engine.gullet.get_conditionals().len() - 1;
1421 let num = engine.read_int(false, &tk)?;
1422 *engine.gullet.get_conditionals().get_mut(index).unwrap() = ActiveConditional::Case(num);
1423 Ok(num == <ET::Num as NumSet>::Int::default())
1424}
1425
1426pub fn ifcat<ET: EngineTypes>(
1427 engine: &mut EngineReferences<ET>,
1428 tk: ET::Token,
1429) -> TeXResult<bool, ET> {
1430 let first = super::methods::get_if_token(engine, &tk)?;
1431 let second = super::methods::get_if_token(engine, &tk)?;
1432 Ok(first.1 == second.1)
1433}
1434
1435pub fn ifdim<ET: EngineTypes>(
1436 engine: &mut EngineReferences<ET>,
1437 tk: ET::Token,
1438) -> TeXResult<bool, ET> {
1439 let first = engine.read_dim(false, &tk)?;
1440 let rel = match engine.read_chars(b"=<>")? {
1441 either::Left(b) => b,
1442 _ => {
1443 TeXError::missing_keyword(engine.aux, engine.state, engine.mouth, &["=", "<", ">"])?;
1444 b'='
1445 }
1446 };
1447 let second = engine.read_dim(false, &tk)?;
1448 Ok(match rel {
1449 b'=' => first == second,
1450 b'<' => first < second,
1451 b'>' => first > second,
1452 _ => unreachable!(),
1453 })
1454}
1455
1456pub fn ifeof<ET: EngineTypes>(
1457 engine: &mut EngineReferences<ET>,
1458 tk: ET::Token,
1459) -> TeXResult<bool, ET> {
1460 let idx = engine.read_file_index(&tk)?;
1461 Ok(engine.filesystem.eof(idx))
1462}
1463
1464pub fn ifhmode<ET: EngineTypes>(
1465 engine: &mut EngineReferences<ET>,
1466 _tk: ET::Token,
1467) -> TeXResult<bool, ET> {
1468 Ok(matches!(
1469 engine.stomach.data_mut().mode(),
1470 TeXMode::Horizontal | TeXMode::RestrictedHorizontal
1471 ))
1472}
1473pub fn ifinner<ET: EngineTypes>(
1474 engine: &mut EngineReferences<ET>,
1475 _tk: ET::Token,
1476) -> TeXResult<bool, ET> {
1477 Ok(matches!(
1478 engine.stomach.data_mut().mode(),
1479 TeXMode::RestrictedHorizontal | TeXMode::InternalVertical | TeXMode::InlineMath
1480 ))
1481}
1482pub fn ifmmode<ET: EngineTypes>(
1483 engine: &mut EngineReferences<ET>,
1484 _tk: ET::Token,
1485) -> TeXResult<bool, ET> {
1486 Ok(matches!(
1487 engine.stomach.data_mut().mode(),
1488 TeXMode::InlineMath | TeXMode::DisplayMath
1489 ))
1490}
1491
1492pub fn ifnum<ET: EngineTypes>(
1493 engine: &mut EngineReferences<ET>,
1494 tk: ET::Token,
1495) -> TeXResult<bool, ET> {
1496 let first = engine.read_int(false, &tk)?;
1497 let rel = match engine.read_chars(b"=<>")? {
1498 either::Left(b) => b,
1499 _ => {
1500 TeXError::missing_keyword(engine.aux, engine.state, engine.mouth, &["=", "<", ">"])?;
1501 b'='
1502 }
1503 };
1504 let second = engine.read_int(false, &tk)?;
1505 Ok(match rel {
1506 b'=' => first == second,
1507 b'<' => first < second,
1508 b'>' => first > second,
1509 _ => unreachable!(),
1510 })
1511}
1512
1513pub fn ifodd<ET: EngineTypes>(
1514 engine: &mut EngineReferences<ET>,
1515 tk: ET::Token,
1516) -> TeXResult<bool, ET> {
1517 Ok(engine.read_int(false, &tk)?.into() % 2 != 0)
1518}
1519
1520pub fn iftrue<ET: EngineTypes>(
1521 _engine: &mut EngineReferences<ET>,
1522 _tk: ET::Token,
1523) -> TeXResult<bool, ET> {
1524 Ok(true)
1525}
1526
1527pub fn iffalse<ET: EngineTypes>(
1528 _engine: &mut EngineReferences<ET>,
1529 _tk: ET::Token,
1530) -> TeXResult<bool, ET> {
1531 Ok(false)
1532}
1533
1534pub fn ifvmode<ET: EngineTypes>(
1535 engine: &mut EngineReferences<ET>,
1536 _tk: ET::Token,
1537) -> TeXResult<bool, ET> {
1538 Ok(matches!(
1539 engine.stomach.data_mut().mode(),
1540 TeXMode::Vertical | TeXMode::InternalVertical
1541 ))
1542}
1543pub fn ifvbox<ET: EngineTypes>(
1544 engine: &mut EngineReferences<ET>,
1545 tk: ET::Token,
1546) -> TeXResult<bool, ET> {
1547 let idx = engine.read_register_index(false, &tk)?;
1548 Ok(matches!(
1549 engine.state.get_box_register(idx),
1550 Some(TeXBox::V { .. })
1551 ))
1552}
1553pub fn ifvoid<ET: EngineTypes>(
1554 engine: &mut EngineReferences<ET>,
1555 tk: ET::Token,
1556) -> TeXResult<bool, ET> {
1557 let idx = engine.read_register_index(false, &tk)?;
1558 Ok(engine.state.get_box_register(idx).is_none())
1559}
1560pub fn ifhbox<ET: EngineTypes>(
1561 engine: &mut EngineReferences<ET>,
1562 tk: ET::Token,
1563) -> TeXResult<bool, ET> {
1564 let idx = engine.read_register_index(false, &tk)?;
1565 Ok(matches!(
1566 engine.state.get_box_register(idx),
1567 Some(TeXBox::H { .. })
1568 ))
1569}
1570
1571pub fn ifx<ET: EngineTypes>(
1572 engine: &mut EngineReferences<ET>,
1573 tk: ET::Token,
1574) -> TeXResult<bool, ET> {
1575 let first = IfxCmd::read(engine, &tk)?;
1576 let second = IfxCmd::read(engine, &tk)?;
1577 Ok(first == second)
1578}
1579
1580pub fn ignorespaces<ET: EngineTypes>(
1581 engine: &mut EngineReferences<ET>,
1582 _tk: ET::Token,
1583) -> TeXResult<(), ET> {
1584 while let Some(next) = engine.mouth.get_next(engine.aux, engine.state)? {
1585 if next.command_code() != CommandCode::Space {
1586 match ET::Gullet::char_or_primitive(engine.state, &next) {
1587 Some(CharOrPrimitive::Char(_, Some(CommandCode::Space))) => (),
1588 _ => {
1589 engine.mouth.requeue(next);
1590 break;
1591 }
1592 }
1593 }
1594 }
1595 Ok(())
1596}
1597
1598pub fn insert<ET: EngineTypes>(
1599 engine: &mut EngineReferences<ET>,
1600 tk: ET::Token,
1601) -> TeXResult<(), ET> {
1602 let n = engine.read_int(false, &tk)?.into();
1603 if n < 0 {
1604 return engine.general_error(format!("Illegal insert index {n}"));
1605 }
1606 engine.expand_until_bgroup(false, &tk)?;
1607 engine
1608 .state
1609 .push(engine.aux, GroupType::Insert, engine.mouth.line_number());
1610 engine
1611 .stomach
1612 .data_mut()
1613 .open_lists
1614 .push(NodeList::Vertical {
1615 children: vec![],
1616 tp: VerticalNodeListType::Insert(n as usize),
1617 });
1618 Ok(())
1619}
1620
1621pub fn immediate<ET: EngineTypes>(
1622 engine: &mut EngineReferences<ET>,
1623 _tk: ET::Token,
1624) -> TeXResult<(), ET> {
1625 expand_loop!(engine,token,
1626 ResolvedToken::Cmd(Some(TeXCommand::Primitive{cmd:PrimitiveCommand::Whatsit { immediate,.. },..})) => {
1627 return immediate(engine,token)
1628 },
1629 ResolvedToken::Tk {char,code} => return ET::Stomach::do_char(engine,token,char,code),
1630 ResolvedToken::Cmd(Some(TeXCommand::Char {char,code})) => return ET::Stomach::do_char(engine,token,*char,*code),
1631 ResolvedToken::Cmd(None) =>
1632 return TeXError::undefined(engine.aux,engine.state,engine.mouth,&token),
1633 ResolvedToken::Cmd(Some(c)) => {
1634 crate::do_cmd!(engine,token,c);
1635 return Ok(())
1636 }
1637 );
1638 Ok(())
1639}
1640
1641pub fn input<ET: EngineTypes>(
1642 engine: &mut EngineReferences<ET>,
1643 tk: ET::Token,
1644) -> TeXResult<(), ET> {
1645 let mut filename = engine.aux.memory.get_string();
1646 engine.read_string(false, &mut filename, &tk)?;
1647 if filename.is_empty() {
1648 return engine.general_error("empty file name in \\input".to_string());
1649 }
1650 let is_file = !filename.starts_with('|');
1651 let file = engine.filesystem.get(&filename);
1652 engine.aux.memory.return_string(filename);
1653 if is_file && !file.exists() {
1654 return engine.general_error(format!("File does not exist: {}", file.path().display()));
1655 }
1656 engine.aux.outputs.file_open(&file);
1657 engine.push_file(file);
1658 Ok(())
1659}
1660
1661pub fn fi<ET: EngineTypes>(engine: &mut EngineReferences<ET>, tk: ET::Token) -> TeXResult<(), ET> {
1662 let conds = engine.gullet.get_conditionals();
1663 let name = match conds.pop() {
1664 Some(ActiveConditional::True(id) | ActiveConditional::Else(id)) => id,
1665 Some(ActiveConditional::Case(_)) => PRIMITIVES.ifcase,
1666 Some(u @ ActiveConditional::Unfinished(_)) => {
1667 conds.push(u);
1668 engine.mouth.requeue(tk);
1669 let relax = engine.aux.memory.cs_interner_mut().cs_from_str("relax");
1670 engine.mouth.requeue(ET::Token::from_cs(relax));
1671 return Ok(());
1672 }
1673 _ => return engine.general_error("Unexpected \\fi".to_string()),
1674 };
1675 let trace = engine.state.get_primitive_int(PRIMITIVES.tracingifs) > ET::Int::default();
1676 let index = conds.len() + 1;
1677 if trace {
1678 engine.aux.outputs.write_neg1(format_args!(
1679 "{{{}fi: {} (level {}) entered on line {}}}",
1680 <ET::Char as Character>::display_opt(engine.state.get_escape_char()),
1681 name.display(engine.state.get_escape_char()),
1682 index,
1683 engine.mouth.line_number()
1684 ));
1685 }
1686 Ok(())
1687}
1688
1689pub fn jobname<ET: EngineTypes>(
1690 engine: &mut EngineReferences<ET>,
1691 exp: &mut Vec<ET::Token>,
1692 _tk: ET::Token,
1693) -> TeXResult<(), ET> {
1694 let mut fi = |t| exp.push(t);
1695 let mut f = Otherize::new(&mut fi);
1696 let escape = engine
1697 .aux
1698 .jobname
1699 .as_bytes()
1700 .iter()
1701 .any(|c| c.is_ascii_whitespace());
1702 if escape {
1703 write!(f, "\"{}\"", engine.aux.jobname)?;
1704 } else {
1705 write!(f, "{}", engine.aux.jobname)?;
1706 }
1707 Ok(())
1708}
1709
1710pub fn fontname<ET: EngineTypes>(
1711 engine: &mut EngineReferences<ET>,
1712 exp: &mut Vec<ET::Token>,
1713 tk: ET::Token,
1714) -> TeXResult<(), ET> {
1715 let font = engine.read_font(false, &tk)?;
1716 let mut fi = |t| exp.push(t);
1717 let mut f = Otherize::new(&mut fi);
1718 if font.has_at_set() {
1719 write!(f, "{} at {}", font.filename(), font.get_at())?;
1720 } else {
1721 write!(f, "{}", font.filename())?;
1722 }
1723 Ok(())
1724}
1725
1726pub fn let_<ET: EngineTypes>(
1727 engine: &mut EngineReferences<ET>,
1728 tk: ET::Token,
1729 globally: bool,
1730) -> TeXResult<(), ET> {
1731 let t = engine.need_next(false, &tk)?;
1732 let cm = match t.to_enum() {
1733 StandardToken::Character(c, CommandCode::Active) => CSOrActiveChar::Active(c),
1734 StandardToken::ControlSequence(cs) => CSOrActiveChar::Name(cs),
1735 _ => return engine.general_error("Control sequence expected after \\let".to_string()),
1736 };
1737 let mut after_eq = false;
1738 let mut after_space = false;
1739 loop {
1740 let next = engine.need_next(false, &tk)?;
1741 let cmd = match next.to_enum() {
1742 StandardToken::Character(_, CommandCode::Space) if !after_eq => continue,
1743 StandardToken::Character(_, CommandCode::Space) if !after_space => {
1744 after_space = true;
1745 continue;
1746 }
1747 StandardToken::Character(c, CommandCode::Other) if matches!(c.try_into(), Ok(b'=')) => {
1748 after_eq = true;
1749 continue;
1750 }
1751 StandardToken::ControlSequence(cs) => engine.state.get_command(&cs).cloned(),
1752 StandardToken::Primitive(id) => engine.state.primitives().get_id(id).cloned(),
1753 StandardToken::Character(c, CommandCode::Active) => {
1754 engine.state.get_ac_command(c).cloned()
1755 }
1756 StandardToken::Character(c, cc) => Some(TeXCommand::Char { char: c, code: cc }),
1757 };
1758 engine.set_command(&cm, cmd, globally);
1759 return Ok(());
1760 }
1761}
1762
1763pub fn futurelet<ET: EngineTypes>(
1764 engine: &mut EngineReferences<ET>,
1765 _tk: ET::Token,
1766 globally: bool,
1767) -> TeXResult<(), ET> {
1768 let t = engine.need_next(false, &_tk)?;
1769 let cm = match t.to_enum() {
1770 StandardToken::Character(c, CommandCode::Active) => CSOrActiveChar::Active(c),
1771 StandardToken::ControlSequence(cs) => CSOrActiveChar::Name(cs),
1772 _ => {
1773 return Err(TeXError::General(
1774 "Control sequence expected after \\futurelet".to_string(),
1775 ))
1776 }
1777 };
1778 let first = match engine.mouth.get_next(engine.aux, engine.state)? {
1779 Some(t) => t,
1780 _ => {
1781 return Err(TeXError::FileEndedWhileScanningUseOf(
1782 "futurelet".to_string(),
1783 ))
1784 }
1785 };
1786 let second = match engine.mouth.get_next(engine.aux, engine.state)? {
1787 Some(t) => t,
1788 _ => {
1789 return Err(TeXError::FileEndedWhileScanningUseOf(
1790 "futurelet".to_string(),
1791 ))
1792 }
1793 };
1794 let cmd = match second.to_enum() {
1795 StandardToken::ControlSequence(cs) => engine.state.get_command(&cs).cloned(),
1796 StandardToken::Character(c, CommandCode::Active) => engine.state.get_ac_command(c).cloned(),
1797 StandardToken::Character(c, cc) => Some(TeXCommand::Char { char: c, code: cc }),
1798 StandardToken::Primitive(id) => engine.state.primitives().get_id(id).cloned(),
1799 };
1800 engine.set_command(&cm, cmd, globally);
1801 engine.mouth.requeue(second);
1802 engine.mouth.requeue(first);
1803 Ok(())
1804}
1805
1806pub fn lowercase<ET: EngineTypes>(
1807 engine: &mut EngineReferences<ET>,
1808 tk: ET::Token,
1809) -> TeXResult<(), ET> {
1810 engine.expand_until_bgroup(false, &tk)?;
1811 let mut exp = Vec::new(); engine.read_until_endgroup(&tk, |_, state, t| match t.to_enum() {
1813 StandardToken::Character(c, cc) => {
1814 let lccode = state.get_lccode(c);
1815 if lccode == ET::Char::default() {
1816 exp.push(t)
1817 } else {
1818 exp.push(ET::Token::from_char_cat(lccode, cc))
1819 }
1820 Ok(())
1821 }
1822 _ => {
1823 exp.push(t);
1824 Ok(())
1825 }
1826 })?;
1827 engine.mouth.push_vec(exp);
1828 Ok(())
1829}
1830
1831pub fn uppercase<ET: EngineTypes>(
1832 engine: &mut EngineReferences<ET>,
1833 tk: ET::Token,
1834) -> TeXResult<(), ET> {
1835 engine.expand_until_bgroup(false, &tk)?;
1836 let mut exp = Vec::new(); engine.read_until_endgroup(&tk, |_, state, t| match t.to_enum() {
1838 StandardToken::Character(c, cc) => {
1839 let uccode = state.get_uccode(c);
1840 if uccode == ET::Char::default() {
1841 exp.push(t)
1842 } else {
1843 exp.push(ET::Token::from_char_cat(uccode, cc))
1844 }
1845 Ok(())
1846 }
1847 _ => {
1848 exp.push(t);
1849 Ok(())
1850 }
1851 })?;
1852 engine.mouth.push_vec(exp);
1853 Ok(())
1854}
1855
1856pub fn mathchardef<ET: EngineTypes>(
1857 engine: &mut EngineReferences<ET>,
1858 tk: ET::Token,
1859 globally: bool,
1860) -> TeXResult<(), ET> {
1861 let t = engine.need_next(false, &tk)?;
1862 let cm = match t.to_enum() {
1863 StandardToken::Character(c, CommandCode::Active) => CSOrActiveChar::Active(c),
1864 StandardToken::ControlSequence(cs) => CSOrActiveChar::Name(cs),
1865 _ => {
1866 return Err(TeXError::General(
1867 "Expected control sequence after \\mathchardef".to_string(),
1868 ))
1869 }
1870 };
1871 let i = engine.read_int(true, &tk)?.into();
1872 if i < 0 || i > u32::MAX as i64 {
1873 return engine.general_error(format!("Illegal math char: {i}"));
1874 }
1875 let i = i as u32;
1876 engine.set_command(&cm, Some(TeXCommand::MathChar(i)), globally);
1877 Ok(())
1878}
1879
1880pub fn mathchar<ET: EngineTypes>(
1881 engine: &mut EngineReferences<ET>,
1882 tk: ET::Token,
1883) -> TeXResult<(), ET> {
1884 let i = engine.read_int(false, &tk)?.into();
1885 if i < 0 || i > 32767 as i64 {
1886 return engine.general_error(format!("Illegal math char: {i}"));
1887 }
1888 let i = i as u32;
1889 let ret = MathChar::from_u32(i, engine.state, None);
1890 ET::Stomach::add_node_m(engine, MathNode::Atom(ret.to_atom()));
1891 Ok(())
1892}
1893
1894pub fn left<ET: EngineTypes>(
1895 engine: &mut EngineReferences<ET>,
1896 tk: ET::Token,
1897) -> TeXResult<(), ET> {
1898 let del = engine.read_opt_delimiter(&tk)?;
1899 engine.stomach.data_mut().open_lists.push(NodeList::Math {
1900 children: MathNodeList::default(),
1901 start: engine.mouth.start_ref(),
1902 tp: MathNodeListType::LeftRight(del),
1903 });
1904 engine
1905 .state
1906 .push(engine.aux, GroupType::LeftRight, engine.mouth.line_number());
1907 Ok(())
1908}
1909pub fn right<ET: EngineTypes>(
1910 engine: &mut EngineReferences<ET>,
1911 tk: ET::Token,
1912) -> TeXResult<(), ET> {
1913 if engine.state.get_group_type() != Some(GroupType::LeftRight) {
1914 return Err(TeXError::General(
1915 "\\right outside of \\left\\right group".to_string(),
1916 ));
1917 }
1918 let del = engine.read_opt_delimiter(&tk)?;
1919 match engine.stomach.data_mut().open_lists.pop() {
1920 Some(NodeList::Math {
1921 children,
1922 start,
1923 tp: MathNodeListType::LeftRight(left),
1924 }) => {
1925 engine.state.pop(engine.aux, engine.mouth);
1926 let (children, None) = children.close(start, engine.mouth.current_sourceref()) else {
1927 unreachable!()
1928 };
1929 ET::Stomach::add_node_m(
1930 engine,
1931 MathNode::Atom(MathAtom {
1932 nucleus: MathNucleus::LeftRight {
1933 left: left.map(|d| (d.large.char, d.large.style)),
1934 right: del.map(|d| (d.large.char, d.large.style)),
1935 start,
1936 end: engine.mouth.current_sourceref(),
1937 children: children.into(),
1938 },
1939 sub: None,
1940 sup: None,
1941 }),
1942 );
1943 }
1944 _ => {
1945 return Err(TeXError::General(
1946 "Unexpected open list in \\right".to_string(),
1947 ))
1948 }
1949 }
1950 Ok(())
1951}
1952
1953pub fn meaning<ET: EngineTypes>(
1954 engine: &mut EngineReferences<ET>,
1955 exp: &mut Vec<ET::Token>,
1956 tk: ET::Token,
1957) -> TeXResult<(), ET> {
1958 let mut fi = |t| exp.push(t);
1959 let mut f = Otherize::new(&mut fi);
1960 let t = engine.need_next(false, &tk)?;
1961 match engine.resolve(&t) {
1962 ResolvedToken::Cmd(None) => {
1963 if let Some(c) = engine.state.get_escape_char() {
1964 f.push_char(c);
1965 }
1966 write!(f, "undefined")?;
1967 }
1968 ResolvedToken::Cmd(Some(cmd)) => cmd
1969 .meaning(
1970 engine.aux.memory.cs_interner(),
1971 engine.state.get_catcode_scheme(),
1972 engine.state.get_escape_char(),
1973 )
1974 .write_chars(&mut f)?,
1975 ResolvedToken::Tk { char, code, .. } => code.meaning(char, f)?,
1976 }
1977 Ok(())
1978}
1979
1980pub fn leaders<ET: EngineTypes>(
1981 engine: &mut EngineReferences<ET>,
1982 tk: ET::Token,
1983) -> TeXResult<(), ET> {
1984 super::methods::do_leaders(engine, LeaderType::Normal, &tk)
1985}
1986pub fn xleaders<ET: EngineTypes>(
1987 engine: &mut EngineReferences<ET>,
1988 tk: ET::Token,
1989) -> TeXResult<(), ET> {
1990 super::methods::do_leaders(engine, LeaderType::X, &tk)
1991}
1992pub fn cleaders<ET: EngineTypes>(
1993 engine: &mut EngineReferences<ET>,
1994 tk: ET::Token,
1995) -> TeXResult<(), ET> {
1996 super::methods::do_leaders(engine, LeaderType::C, &tk)
1997}
1998
1999pub fn message<ET: EngineTypes>(
2000 engine: &mut EngineReferences<ET>,
2001 tk: ET::Token,
2002) -> TeXResult<(), ET> {
2003 let mut out = engine.aux.memory.get_string();
2004 engine.read_braced_string(false, true, &tk, &mut out)?;
2005 engine.aux.outputs.message(&out);
2006 engine.aux.memory.return_string(out);
2007 Ok(())
2008}
2009
2010pub fn errmessage<ET: EngineTypes>(
2011 engine: &mut EngineReferences<ET>,
2012 tk: ET::Token,
2013) -> TeXResult<(), ET> {
2014 let mut out = String::new();
2015 engine.read_braced_string(false, true, &tk, &mut out)?;
2016 write!(out, " (line {})", engine.mouth.line_number()).unwrap();
2017 engine.general_error(out)
2018}
2019
2020pub fn newlinechar_get<ET: EngineTypes>(
2021 engine: &mut EngineReferences<ET>,
2022 _tk: ET::Token,
2023) -> TeXResult<ET::Int, ET> {
2024 Ok(ET::Int::from(match engine.state.get_newline_char() {
2025 Some(c) => c.into() as i32,
2026 _ => -1,
2027 }))
2028}
2029pub fn newlinechar_set<ET: EngineTypes>(
2030 engine: &mut EngineReferences<ET>,
2031 tk: ET::Token,
2032 globally: bool,
2033) -> TeXResult<(), ET> {
2034 let val: i64 = engine.read_int(true, &tk)?.into();
2035 let val = match val.cmp(&-1) {
2036 Ordering::Less => return engine.general_error(format!("Illegal newline character: {val}")),
2037 Ordering::Equal => None,
2038 Ordering::Greater => match ET::Char::try_from(val as u64) {
2039 Ok(c) => Some(c),
2040 _ => return engine.general_error(format!("Illegal newline character: {val}")),
2041 },
2042 };
2043 engine.state.set_newline_char(engine.aux, val, globally);
2044 Ok(())
2045}
2046
2047pub fn number<ET: EngineTypes>(
2048 engine: &mut EngineReferences<ET>,
2049 exp: &mut Vec<ET::Token>,
2050 tk: ET::Token,
2051) -> TeXResult<(), ET> {
2052 let val = engine.read_int(false, &tk)?;
2053 write!(Otherize::new(&mut |t| exp.push(t)), "{}", val)?;
2054 Ok(())
2055}
2056
2057pub fn noexpand<ET: EngineTypes>(
2058 engine: &mut EngineReferences<ET>,
2059 _tk: ET::Token,
2060) -> TeXResult<(), ET> {
2061 let token = match engine.mouth.get_next(engine.aux, engine.state)? {
2062 Some(t) if t == ET::Token::eof() => return Ok(()),
2063 Some(t) => t,
2064 _ => {
2065 return Err(TeXError::General(
2066 "control sequence expected after \\noexpand".to_string(),
2067 ))
2068 }
2069 };
2070 match engine.resolve(&token) {
2071 ResolvedToken::Tk {
2072 code: CommandCode::AlignmentTab,
2073 ..
2074 } => {
2075 engine.mouth.requeue(token);
2076 engine
2077 .mouth
2078 .requeue(ET::Token::primitive(PRIMITIVES.noexpand));
2079 }
2080 ResolvedToken::Tk { .. } => engine.mouth.requeue(token),
2081 ResolvedToken::Cmd(Some(cm)) => {
2082 match cm {
2083 TeXCommand::Macro(_)
2084 | TeXCommand::Primitive {
2085 cmd:
2086 PrimitiveCommand::Expandable(_)
2087 | PrimitiveCommand::SimpleExpandable(_)
2088 | PrimitiveCommand::Conditional(_),
2089 ..
2090 } => {
2091 engine.mouth.requeue(token);
2092 engine
2093 .mouth
2094 .requeue(ET::Token::primitive(PRIMITIVES.noexpand));
2095 }
2096 TeXCommand::Primitive { name, .. }
2097 if *name == PRIMITIVES.cr || *name == PRIMITIVES.crcr =>
2098 {
2099 engine.mouth.requeue(token);
2100 engine
2101 .mouth
2102 .requeue(ET::Token::primitive(PRIMITIVES.noexpand));
2103 }
2104 _ => engine.mouth.requeue(token),
2105 };
2106 }
2107 ResolvedToken::Cmd(_) => engine.mouth.requeue(token),
2108 }
2109 Ok(())
2110}
2111
2112pub fn noindent<ET: EngineTypes>(
2113 engine: &mut EngineReferences<ET>,
2114 tk: ET::Token,
2115) -> TeXResult<(), ET> {
2116 if ET::Stomach::maybe_switch_mode(
2117 engine,
2118 CommandScope::SwitchesToHorizontal,
2119 tk,
2120 PRIMITIVES.noindent,
2121 )? {
2122 match engine.stomach.data_mut().open_lists.last_mut() {
2123 Some(NodeList::Horizontal { children, .. }) => {
2124 if let Some(HNode::Box(TeXBox::H {
2125 info: HBoxInfo::ParIndent { .. },
2126 ..
2127 })) = children.last_mut()
2128 {
2129 children.pop();
2130 }
2131 }
2132 _ => unreachable!(),
2133 }
2134 }
2135 Ok(())
2136}
2137
2138pub fn openout<ET: EngineTypes>(
2139 engine: &mut EngineReferences<ET>,
2140 tk: ET::Token,
2141) -> TeXResult<Option<Box<WhatsitFunction<ET>>>, ET> {
2142 let (idx, file) = engine.read_filename_and_index("./", &tk)?;
2143 Ok(Some(Box::new(move |engine| {
2144 engine.filesystem.open_out(idx, file);
2145 Ok(())
2146 })))
2147}
2148pub fn openout_immediate<ET: EngineTypes>(
2149 engine: &mut EngineReferences<ET>,
2150 tk: ET::Token,
2151) -> TeXResult<(), ET> {
2152 let (idx, file) = engine.read_filename_and_index("./", &tk)?;
2153 engine.filesystem.open_out(idx, file);
2154 Ok(())
2155}
2156
2157pub fn prevdepth_get<ET: EngineTypes>(
2158 engine: &mut EngineReferences<ET>,
2159 _tk: ET::Token,
2160) -> TeXResult<ET::Dim, ET> {
2161 Ok(engine.stomach.data_mut().prevdepth)
2162}
2163pub fn prevdepth_set<ET: EngineTypes>(
2164 engine: &mut EngineReferences<ET>,
2165 tk: ET::Token,
2166 _globally: bool,
2167) -> TeXResult<(), ET> {
2168 let val = engine.read_dim(true, &tk)?;
2169 engine.stomach.data_mut().prevdepth = val;
2170 Ok(())
2171}
2172
2173pub fn read<ET: EngineTypes>(
2174 engine: &mut EngineReferences<ET>,
2175 tk: ET::Token,
2176 globally: bool,
2177) -> TeXResult<(), ET> {
2178 let idx = engine.read_file_index(&tk)?;
2179 if !engine.read_keyword("to".as_bytes())? {
2180 TeXError::missing_keyword(engine.aux, engine.state, engine.mouth, &["to"])?;
2181 }
2182 let cs = engine.read_control_sequence(&tk)?;
2183 let mut ret = shared_vector::Vector::new();
2184 engine.filesystem.read::<ET, _>(
2185 idx,
2186 engine.aux.memory.cs_interner_mut(),
2187 engine.state,
2188 |t| ret.push(t),
2189 )?;
2190
2191 let m = Macro {
2192 long: false,
2193 outer: false,
2194 protected: false,
2195 expansion: ret.into(),
2196 signature: MacroSignature {
2197 arity: 0,
2198 params: engine.aux.memory.empty_list(),
2199 },
2200 };
2201 engine.set_command(&cs, Some(TeXCommand::Macro(m)), globally);
2202 Ok(())
2203}
2204
2205const ROMAN: &[(u8, i64)] = &[
2206 (b'm', 0),
2207 (b'd', 2),
2208 (b'c', 5),
2209 (b'l', 2),
2210 (b'x', 5),
2211 (b'v', 2),
2212 (b'i', 5),
2213];
2214#[allow(clippy::many_single_char_names)]
2215pub fn romannumeral<ET: EngineTypes>(
2216 engine: &mut EngineReferences<ET>,
2217 exp: &mut Vec<ET::Token>,
2218 tk: ET::Token,
2219) -> TeXResult<(), ET> {
2220 let mut n = engine.read_int(false, &tk)?.into();
2221 if n < 0 {
2222 return Ok(());
2223 }
2224 let mut v = 1000;
2225 let mut j = 0;
2226 loop {
2227 while n >= v {
2228 exp.push(ET::Token::from_char_cat(
2229 ET::Char::from(ROMAN[j].0),
2230 CommandCode::Other,
2231 ));
2232 n -= v;
2233 }
2234 if n <= 0 {
2235 return Ok(());
2236 }
2237 let mut k = ROMAN[j + 1];
2238 let mut u = v / k.1;
2239 if k.1 == 2 {
2240 k = ROMAN[j + 2];
2241 u /= k.1;
2242 }
2243 if n + u >= v {
2244 exp.push(ET::Token::from_char_cat(
2245 ET::Char::from(k.0),
2246 CommandCode::Other,
2247 ));
2248 n += u;
2249 } else {
2250 j += 1;
2251 v /= ROMAN[j].1;
2252 }
2253 }
2254}
2255
2256pub fn setbox<ET: EngineTypes>(
2257 engine: &mut EngineReferences<ET>,
2258 tk: ET::Token,
2259 globally: bool,
2260) -> TeXResult<(), ET> {
2261 let index = engine.read_register_index(false, &tk)?;
2262 match engine.read_box(true)? {
2263 either::Left(bx) => engine
2264 .state
2265 .set_box_register(engine.aux, index, bx, globally),
2266 either::Right(bi) => {
2267 let target = BoxTarget::<ET>::new(move |e, b| {
2268 e.state.set_box_register(e.aux, index, Some(b), globally);
2269 Ok(())
2270 });
2271 let mut ls = bi.open_list(engine.mouth.start_ref());
2272 match ls {
2273 NodeList::Horizontal {
2274 tp: HorizontalNodeListType::Box(_, _, ref mut t),
2275 ..
2276 } => *t = target,
2277 NodeList::Vertical {
2278 tp: VerticalNodeListType::Box(_, _, ref mut t),
2279 ..
2280 } => *t = target,
2281 _ => unreachable!(),
2282 }
2283 engine.stomach.data_mut().open_lists.push(ls);
2284 }
2285 }
2286 Ok(())
2287}
2288
2289pub fn moveright<ET: EngineTypes>(
2290 engine: &mut EngineReferences<ET>,
2291 tk: ET::Token,
2292) -> TeXResult<(), ET> {
2293 let dim = engine.read_dim(false, &tk)?;
2294 match engine.read_box(false)? {
2295 either::Left(Some(mut bx)) => {
2296 match bx {
2297 TeXBox::H { ref mut info, .. } => info.move_left(-dim),
2298 TeXBox::V { ref mut info, .. } => info.move_left(-dim),
2299 }
2300 ET::Stomach::add_node_v(engine, VNode::Box(bx))?;
2301 }
2302 either::Left(None) => (),
2303 either::Right(mut bi) => {
2304 bi.move_left(-dim);
2305 engine
2306 .stomach
2307 .data_mut()
2308 .open_lists
2309 .push(bi.open_list(engine.mouth.start_ref()));
2310 }
2311 }
2312 Ok(())
2313}
2314
2315pub fn moveleft<ET: EngineTypes>(
2316 engine: &mut EngineReferences<ET>,
2317 tk: ET::Token,
2318) -> TeXResult<(), ET> {
2319 let dim = engine.read_dim(false, &tk)?;
2320 match engine.read_box(false)? {
2321 either::Left(Some(mut bx)) => {
2322 match bx {
2323 TeXBox::H { ref mut info, .. } => info.move_left(dim),
2324 TeXBox::V { ref mut info, .. } => info.move_left(dim),
2325 }
2326 ET::Stomach::add_node_v(engine, VNode::Box(bx))?;
2327 }
2328 either::Left(None) => (),
2329 either::Right(mut bi) => {
2330 bi.move_left(dim);
2331 engine
2332 .stomach
2333 .data_mut()
2334 .open_lists
2335 .push(bi.open_list(engine.mouth.start_ref()));
2336 }
2337 }
2338 Ok(())
2339}
2340
2341pub fn raise<ET: EngineTypes>(
2342 engine: &mut EngineReferences<ET>,
2343 tk: ET::Token,
2344) -> TeXResult<(), ET> {
2345 let dim = engine.read_dim(false, &tk)?;
2346 match engine.read_box(false)? {
2347 either::Left(Some(mut bx)) => {
2348 match bx {
2349 TeXBox::H { ref mut info, .. } => info.raise(dim),
2350 TeXBox::V { ref mut info, .. } => info.raise(dim),
2351 }
2352 match engine.stomach.data_mut().mode() {
2353 TeXMode::Horizontal | TeXMode::RestrictedHorizontal => {
2354 ET::Stomach::add_node_h(engine, HNode::Box(bx));
2355 }
2356 TeXMode::InlineMath | TeXMode::DisplayMath => {
2357 ET::Stomach::add_node_m(engine, bx.to_math());
2358 }
2359 _ => unreachable!(),
2360 }
2361 }
2362 either::Left(None) => (),
2363 either::Right(mut bi) => {
2364 bi.raise(dim);
2365 engine
2366 .stomach
2367 .data_mut()
2368 .open_lists
2369 .push(bi.open_list(engine.mouth.start_ref()));
2370 }
2371 }
2372 Ok(())
2373}
2374
2375pub fn lower<ET: EngineTypes>(
2376 engine: &mut EngineReferences<ET>,
2377 tk: ET::Token,
2378) -> TeXResult<(), ET> {
2379 let dim = engine.read_dim(false, &tk)?;
2380 match engine.read_box(false)? {
2381 either::Left(Some(mut bx)) => {
2382 match bx {
2383 TeXBox::H { ref mut info, .. } => info.raise(-dim),
2384 TeXBox::V { ref mut info, .. } => info.raise(-dim),
2385 }
2386 match engine.stomach.data_mut().mode() {
2387 TeXMode::Horizontal | TeXMode::RestrictedHorizontal => {
2388 ET::Stomach::add_node_h(engine, HNode::Box(bx));
2389 }
2390 TeXMode::InlineMath | TeXMode::DisplayMath => {
2391 ET::Stomach::add_node_m(engine, bx.to_math());
2392 }
2393 _ => unreachable!(),
2394 }
2395 }
2396 either::Left(None) => (),
2397 either::Right(mut bi) => {
2398 bi.raise(-dim);
2399 engine
2400 .stomach
2401 .data_mut()
2402 .open_lists
2403 .push(bi.open_list(engine.mouth.start_ref()));
2404 }
2405 }
2406 Ok(())
2407}
2408
2409pub fn string<ET: EngineTypes>(
2410 engine: &mut EngineReferences<ET>,
2411 exp: &mut Vec<ET::Token>,
2412 tk: ET::Token,
2413) -> TeXResult<(), ET> {
2414 let t = engine.need_next(false, &tk)?;
2415 if t.command_code() == CommandCode::Space {
2416 exp.push(t)
2417 } else {
2418 match t.to_enum() {
2419 StandardToken::Character(c, _) => {
2420 exp.push(ET::Token::from_char_cat(c, CommandCode::Other))
2421 }
2422 StandardToken::ControlSequence(cs) => {
2423 let res = engine.aux.memory.cs_interner().resolve(&cs);
2424 if let Some(c) = engine.state.get_escape_char() {
2425 exp.push(ET::Token::from_char_cat(c, CommandCode::Other))
2426 }
2427 for u in res.iter() {
2428 match u.try_into() {
2429 Ok(b' ') => exp.push(ET::Token::space()),
2430 _ => exp.push(ET::Token::from_char_cat(u, CommandCode::Other)),
2431 }
2432 }
2433 }
2434 _ => return Err(TeXError::EmergencyStop),
2435 }
2436 }
2437 Ok(())
2438}
2439
2440pub fn openin<ET: EngineTypes>(
2441 engine: &mut EngineReferences<ET>,
2442 tk: ET::Token,
2443) -> TeXResult<(), ET> {
2444 let (idx, file) = engine.read_filename_and_index("", &tk)?;
2445 engine.filesystem.open_in(idx, file);
2446 Ok(())
2447}
2448
2449pub fn closeout<ET: EngineTypes>(
2450 engine: &mut EngineReferences<ET>,
2451 tk: ET::Token,
2452) -> TeXResult<Option<Box<WhatsitFunction<ET>>>, ET> {
2453 let idx = engine.read_file_index(&tk)?;
2454 Ok(Some(Box::new(move |engine| {
2455 engine.filesystem.close_out(idx);
2456 Ok(())
2457 })))
2458}
2459pub fn closeout_immediate<ET: EngineTypes>(
2460 engine: &mut EngineReferences<ET>,
2461 tk: ET::Token,
2462) -> TeXResult<(), ET> {
2463 let idx = engine.read_file_index(&tk)?;
2464 engine.filesystem.close_out(idx);
2465 Ok(())
2466}
2467
2468pub fn closein<ET: EngineTypes>(
2469 engine: &mut EngineReferences<ET>,
2470 tk: ET::Token,
2471) -> TeXResult<(), ET> {
2472 let idx = engine.read_file_index(&tk)?;
2473 engine.filesystem.close_in(idx);
2474 Ok(())
2475}
2476
2477pub fn write<ET: EngineTypes>(
2478 engine: &mut EngineReferences<ET>,
2479 tk: ET::Token,
2480) -> TeXResult<Option<Box<WhatsitFunction<ET>>>, ET> {
2481 let idx = engine.read_int(false, &tk)?.into();
2482 let mut tks = Vec::new();
2483 tks.push(ET::Token::from_char_cat(
2484 b'{'.into(),
2485 CommandCode::BeginGroup,
2486 ));
2487 let t = engine.need_next(false, &tk)?;
2488 if t.command_code() != CommandCode::BeginGroup {
2489 TeXError::missing_begingroup(engine.aux, engine.state, engine.mouth)?;
2490 }
2491 engine.read_until_endgroup(&tk, |_, _, t| {
2492 tks.push(t);
2493 Ok(())
2494 })?;
2495 tks.push(ET::Token::from_char_cat(b'}'.into(), CommandCode::EndGroup));
2496 Ok(Some(Box::new(move |engine| do_write(engine, tk, idx, tks))))
2497}
2498
2499pub fn write_immediate<ET: EngineTypes>(
2500 engine: &mut EngineReferences<ET>,
2501 tk: ET::Token,
2502) -> TeXResult<(), ET> {
2503 let idx = engine.read_int(false, &tk)?.into();
2504 let mut out = engine.aux.memory.get_string();
2505 engine.read_braced_string(false, false, &tk, &mut out)?;
2506 engine
2507 .filesystem
2508 .write(idx, &out, engine.state.get_newline_char(), engine.aux);
2509 engine.aux.memory.return_string(out);
2510 Ok(())
2511}
2512pub fn do_write<ET: EngineTypes>(
2513 engine: &mut EngineReferences<ET>,
2514 tk: ET::Token,
2515 i: i64,
2516 v: Vec<ET::Token>,
2517) -> TeXResult<(), ET> {
2518 engine.mouth.push_vec(v);
2519 let mut out = engine.aux.memory.get_string();
2520 engine.read_braced_string(false, false, &tk, &mut out)?;
2521 engine
2522 .filesystem
2523 .write(i, &out, engine.state.get_newline_char(), engine.aux);
2524 engine.aux.memory.return_string(out);
2525 Ok(())
2526}
2527
2528pub fn par<ET: EngineTypes>(
2529 engine: &mut EngineReferences<ET>,
2530 _tk: ET::Token,
2531) -> TeXResult<(), ET> {
2532 let mode = engine.stomach.data_mut().mode();
2533 if mode.is_vertical() {
2534 return Ok(());
2535 }
2536 if mode == TeXMode::Horizontal {
2537 return ET::Stomach::close_paragraph(engine);
2538 }
2539 Err(TeXError::General(
2540 "\\par not allowed in math mode".to_string(),
2541 ))
2542}
2543
2544pub fn the<ET: EngineTypes>(
2545 engine: &mut EngineReferences<ET>,
2546 exp: &mut Vec<ET::Token>,
2547 _tk: ET::Token,
2548) -> TeXResult<(), ET> {
2549 engine.do_the(|_, _, _, t| {
2550 exp.push(t);
2551 Ok(())
2552 })
2553}
2554
2555pub fn toks<ET: EngineTypes>(
2556 engine: &mut EngineReferences<ET>,
2557 tk: ET::Token,
2558 global: bool,
2559) -> TeXResult<(), ET> {
2560 let idx = engine.read_register_index(false, &tk)?;
2561 ET::Stomach::assign_toks_register(engine, tk, idx, global)
2562}
2563
2564pub fn penalty<ET: EngineTypes>(
2565 engine: &mut EngineReferences<ET>,
2566 tk: ET::Token,
2567) -> TeXResult<(), ET> {
2568 let i = match engine.read_int(false, &tk)?.try_into() {
2569 Ok(i) => i,
2570 Err(_) => return engine.general_error("Number expected after \\penalty".to_string()),
2571 };
2572 crate::add_node!(ET::Stomach;engine, VNode::Penalty(i), HNode::Penalty(i), MathNode::Penalty(i));
2573 Ok(())
2574}
2575
2576pub fn kern<ET: EngineTypes>(
2577 engine: &mut EngineReferences<ET>,
2578 tk: ET::Token,
2579) -> TeXResult<(), ET> {
2580 let dim = engine.read_dim(false, &tk)?;
2581 crate::add_node!(ET::Stomach;engine,VNode::VKern(dim), HNode::HKern(dim), MathNode::HKern(dim));
2582 Ok(())
2583}
2584
2585pub fn vrule<ET: EngineTypes>(
2586 engine: &mut EngineReferences<ET>,
2587 tk: ET::Token,
2588) -> TeXResult<(), ET> {
2589 let start = engine.mouth.start_ref();
2590 let mut width = None;
2591 let mut height = None;
2592 let mut depth = None;
2593 loop {
2594 match engine.read_keywords(&[b"width", b"height", b"depth"])? {
2595 Some(b"width") => {
2596 width = Some(engine.read_dim(false, &tk)?);
2597 }
2598 Some(b"height") => {
2599 height = Some(engine.read_dim(false, &tk)?);
2600 }
2601 Some(b"depth") => {
2602 depth = Some(engine.read_dim(false, &tk)?);
2603 }
2604 _ => break,
2605 }
2606 }
2607 let end = engine.mouth.current_sourceref();
2608 match engine.stomach.data_mut().mode() {
2609 TeXMode::Horizontal | TeXMode::RestrictedHorizontal => ET::Stomach::add_node_h(
2610 engine,
2611 HNode::VRule {
2612 width,
2613 height,
2614 depth,
2615 start,
2616 end,
2617 },
2618 ),
2619 TeXMode::InlineMath | TeXMode::DisplayMath => ET::Stomach::add_node_m(
2620 engine,
2621 MathNode::VRule {
2622 width,
2623 height,
2624 depth,
2625 start,
2626 end,
2627 },
2628 ),
2629 _ => unreachable!(),
2630 }
2631 Ok(())
2632}
2633
2634pub fn hrule<ET: EngineTypes>(
2635 engine: &mut EngineReferences<ET>,
2636 tk: ET::Token,
2637) -> TeXResult<(), ET> {
2638 let start = engine.mouth.start_ref();
2639 let mut width = None;
2640 let mut height = None;
2641 let mut depth = None;
2642 loop {
2643 match engine.read_keywords(&[b"width", b"height", b"depth"])? {
2644 Some(b"width") => {
2645 width = Some(engine.read_dim(false, &tk)?);
2646 }
2647 Some(b"height") => {
2648 height = Some(engine.read_dim(false, &tk)?);
2649 }
2650 Some(b"depth") => {
2651 depth = Some(engine.read_dim(false, &tk)?);
2652 }
2653 _ => break,
2654 }
2655 }
2656 let end = engine.mouth.current_sourceref();
2657 ET::Stomach::add_node_v(
2658 engine,
2659 VNode::HRule {
2660 width,
2661 height,
2662 depth,
2663 start,
2664 end,
2665 },
2666 )
2667}
2668
2669pub fn vskip<ET: EngineTypes>(
2670 engine: &mut EngineReferences<ET>,
2671 tk: ET::Token,
2672) -> TeXResult<(), ET> {
2673 let sk = engine.read_skip(false, &tk)?;
2674 ET::Stomach::add_node_v(engine, VNode::VSkip(sk))
2675}
2676
2677pub fn vfil<ET: EngineTypes>(
2678 engine: &mut EngineReferences<ET>,
2679 _tk: ET::Token,
2680) -> TeXResult<(), ET> {
2681 ET::Stomach::add_node_v(engine, VNode::VFil)
2682}
2683pub fn vfill<ET: EngineTypes>(
2684 engine: &mut EngineReferences<ET>,
2685 _tk: ET::Token,
2686) -> TeXResult<(), ET> {
2687 ET::Stomach::add_node_v(engine, VNode::VFill)
2688}
2689pub fn vfilneg<ET: EngineTypes>(
2690 engine: &mut EngineReferences<ET>,
2691 _tk: ET::Token,
2692) -> TeXResult<(), ET> {
2693 ET::Stomach::add_node_v(engine, VNode::VFilneg)
2694}
2695pub fn vss<ET: EngineTypes>(
2696 engine: &mut EngineReferences<ET>,
2697 _tk: ET::Token,
2698) -> TeXResult<(), ET> {
2699 ET::Stomach::add_node_v(engine, VNode::Vss)
2700}
2701
2702#[allow(unreachable_code)]
2703pub fn hskip<ET: EngineTypes>(
2704 engine: &mut EngineReferences<ET>,
2705 tk: ET::Token,
2706) -> TeXResult<(), ET> {
2707 let sk = engine.read_skip(false, &tk)?;
2708 add_node!(ET::Stomach;engine,unreachable!(),HNode::HSkip(sk),MathNode::HSkip(sk));
2709 Ok(())
2710}
2711#[allow(unreachable_code)]
2712pub fn hfil<ET: EngineTypes>(
2713 engine: &mut EngineReferences<ET>,
2714 _tk: ET::Token,
2715) -> TeXResult<(), ET> {
2716 add_node!(ET::Stomach;engine,unreachable!(),HNode::HFil,MathNode::HFil);
2717 Ok(())
2718}
2719#[allow(unreachable_code)]
2720pub fn hfill<ET: EngineTypes>(
2721 engine: &mut EngineReferences<ET>,
2722 _tk: ET::Token,
2723) -> TeXResult<(), ET> {
2724 add_node!(ET::Stomach;engine,unreachable!(),HNode::HFill,MathNode::HFill);
2725 Ok(())
2726}
2727#[allow(unreachable_code)]
2728pub fn hfilneg<ET: EngineTypes>(
2729 engine: &mut EngineReferences<ET>,
2730 _tk: ET::Token,
2731) -> TeXResult<(), ET> {
2732 add_node!(ET::Stomach;engine,unreachable!(),HNode::HFilneg,MathNode::HFilneg);
2733 Ok(())
2734}
2735#[allow(unreachable_code)]
2736pub fn hss<ET: EngineTypes>(
2737 engine: &mut EngineReferences<ET>,
2738 _tk: ET::Token,
2739) -> TeXResult<(), ET> {
2740 add_node!(ET::Stomach;engine,unreachable!(),HNode::Hss,MathNode::Hss);
2741 Ok(())
2742}
2743
2744pub fn indent<ET: EngineTypes>(
2745 engine: &mut EngineReferences<ET>,
2746 _tk: ET::Token,
2747) -> TeXResult<(), ET> {
2748 let dim = engine.state.get_primitive_dim(PRIMITIVES.parindent);
2749 ET::Stomach::add_node_h(
2750 engine,
2751 HNode::Box(TeXBox::H {
2752 children: vec![].into(),
2753 info: HBoxInfo::ParIndent(dim),
2754 start: engine.mouth.start_ref(),
2755 end: engine.mouth.current_sourceref(),
2756 preskip: None,
2757 }),
2758 );
2759 Ok(())
2760}
2761
2762pub fn delimiter<ET: EngineTypes>(
2763 engine: &mut EngineReferences<ET>,
2764 tk: ET::Token,
2765) -> TeXResult<(), ET> {
2766 let num = engine.read_int(false, &tk)?;
2767 let delim = match Delimiter::from_int(num, engine.state) {
2768 either::Left(d) => d,
2769 either::Right((d, i)) => {
2770 engine.general_error(format!("Bad delimiter code ({})", i))?;
2771 d
2772 }
2773 };
2774 ET::Stomach::add_node_m(engine, MathNode::Atom(delim.small.to_atom()));
2775 Ok(())
2776}
2777
2778pub fn mskip<ET: EngineTypes>(
2779 engine: &mut EngineReferences<ET>,
2780 tk: ET::Token,
2781) -> TeXResult<(), ET> {
2782 let skip = engine.read_muskip(false, &tk)?;
2783 ET::Stomach::add_node_m(
2784 engine,
2785 MathNode::MSkip {
2786 skip,
2787 style: UnresolvedMathFontStyle::of_fam(0),
2788 },
2789 );
2790 Ok(())
2791}
2792
2793pub fn mkern<ET: EngineTypes>(
2794 engine: &mut EngineReferences<ET>,
2795 tk: ET::Token,
2796) -> TeXResult<(), ET> {
2797 let kern = engine.read_mudim(false, &tk)?;
2798 ET::Stomach::add_node_m(
2799 engine,
2800 MathNode::MKern {
2801 kern,
2802 style: UnresolvedMathFontStyle::of_fam(0),
2803 },
2804 );
2805 Ok(())
2806}
2807
2808pub fn unskip<ET: EngineTypes>(
2809 engine: &mut EngineReferences<ET>,
2810 _tk: ET::Token,
2811) -> TeXResult<(), ET> {
2812 super::methods::un_x(
2813 engine,
2814 |v| {
2815 matches!(
2816 v,
2817 VNode::VSkip(_) | VNode::VFil | VNode::VFill | VNode::VFilneg | VNode::Vss
2818 )
2819 },
2820 |h| {
2821 matches!(
2822 h,
2823 HNode::HSkip(_) | HNode::HFil | HNode::HFill | HNode::HFilneg | HNode::Hss
2824 )
2825 },
2826 |m| {
2827 matches!(
2828 m,
2829 MathNode::MSkip { .. }
2830 | MathNode::HSkip(_)
2831 | MathNode::HFil
2832 | MathNode::HFill
2833 | MathNode::HFilneg
2834 | MathNode::Hss
2835 )
2836 },
2837 );
2838 Ok(())
2839}
2840pub fn unkern<ET: EngineTypes>(
2841 engine: &mut EngineReferences<ET>,
2842 _tk: ET::Token,
2843) -> TeXResult<(), ET> {
2844 super::methods::un_x(
2845 engine,
2846 |v| matches!(v, VNode::VKern(_)),
2847 |h| matches!(h, HNode::HKern(_)),
2848 |m| matches!(m, MathNode::MKern { .. } | MathNode::HKern(_)),
2849 );
2850 Ok(())
2851}
2852pub fn unpenalty<ET: EngineTypes>(
2853 engine: &mut EngineReferences<ET>,
2854 _tk: ET::Token,
2855) -> TeXResult<(), ET> {
2856 super::methods::un_x(
2857 engine,
2858 |v| matches!(v, VNode::Penalty(_)),
2859 |h| matches!(h, HNode::Penalty(_)),
2860 |m| matches!(m, MathNode::Penalty(_)),
2861 );
2862 Ok(())
2863}
2864
2865pub fn lastbox<ET: EngineTypes>(
2866 engine: &mut EngineReferences<ET>,
2867 _tk: ET::Token,
2868) -> TeXResult<Either<Option<TeXBox<ET>>, BoxInfo<ET>>, ET> {
2869 if engine.stomach.data_mut().mode() == TeXMode::Vertical {
2870 return Err(TeXError::General(
2871 "\\lastbox not allowed outside of vertical mode".to_string(),
2872 ));
2873 }
2874 let data = engine.stomach.data_mut();
2875 match data.open_lists.last_mut() {
2876 None => unreachable!(),
2877 Some(NodeList::Vertical { children, .. }) => {
2878 for (i, n) in children.iter().enumerate().rev() {
2879 if n.opaque() {
2880 continue;
2881 }
2882 match n {
2883 VNode::Box(_) => {
2884 if let VNode::Box(bi) = children.remove(i) {
2885 return Ok(either::Left(Some(bi)));
2886 }
2887 unreachable!()
2888 }
2889 _ => return Ok(either::Left(None)),
2890 }
2891 }
2892 }
2893 Some(NodeList::Horizontal { children, .. }) => {
2894 for (i, n) in children.iter().enumerate().rev() {
2895 if n.opaque() {
2896 continue;
2897 }
2898 match n {
2899 HNode::Box(_) => {
2900 if let HNode::Box(bi) = children.remove(i) {
2901 return Ok(either::Left(Some(bi)));
2902 } else {
2903 unreachable!()
2904 }
2905 }
2906 _ => return Ok(either::Left(None)),
2907 }
2908 }
2909 }
2910 _ => (),
2911 }
2912 Ok(either::Left(None))
2913}
2914
2915pub fn lastkern<ET: EngineTypes>(
2916 engine: &mut EngineReferences<ET>,
2917 _tk: ET::Token,
2918) -> TeXResult<ET::Dim, ET> {
2919 Ok(super::methods::last_x(
2920 engine,
2921 |v| match v {
2922 VNode::VKern(k) => Some(*k),
2923 _ => None,
2924 },
2925 |h| match h {
2926 HNode::HKern(k) => Some(*k),
2927 _ => None,
2928 },
2929 |m| match m {
2930 MathNode::HKern(k) => Some(*k),
2931 _ => None,
2932 },
2933 )
2934 .unwrap_or_default())
2935}
2936
2937pub fn lastskip<ET: EngineTypes>(
2938 engine: &mut EngineReferences<ET>,
2939 _tk: ET::Token,
2940) -> TeXResult<Skip<ET::Dim>, ET> {
2941 Ok(super::methods::last_x(
2942 engine,
2943 |v| match v {
2944 VNode::VSkip(k) => Some(*k),
2945 _ => None,
2946 },
2947 |h| match h {
2948 HNode::HSkip(k) => Some(*k),
2949 _ => None,
2950 },
2951 |m| match m {
2952 MathNode::HSkip(k) => Some(*k),
2953 _ => None,
2954 },
2955 )
2956 .unwrap_or_default())
2957}
2958
2959pub fn lastpenalty<ET: EngineTypes>(
2960 engine: &mut EngineReferences<ET>,
2961 _tk: ET::Token,
2962) -> TeXResult<ET::Int, ET> {
2963 Ok(super::methods::last_x(
2964 engine,
2965 |v| match v {
2966 VNode::Penalty(k) => Some(*k),
2967 _ => None,
2968 },
2969 |h| match h {
2970 HNode::Penalty(k) => Some(*k),
2971 _ => None,
2972 },
2973 |m| match m {
2974 MathNode::Penalty(k) => Some(*k),
2975 _ => None,
2976 },
2977 )
2978 .unwrap_or_default()
2979 .into())
2980}
2981
2982pub fn pagegoal_get<ET: EngineTypes>(
2983 engine: &mut EngineReferences<ET>,
2984 _tk: ET::Token,
2985) -> TeXResult<ET::Dim, ET> {
2986 Ok(engine.stomach.data_mut().pagegoal)
2987}
2988pub fn pagegoal_set<ET: EngineTypes>(
2989 engine: &mut EngineReferences<ET>,
2990 tk: ET::Token,
2991 _globally: bool,
2992) -> TeXResult<(), ET> {
2993 let d = engine.read_dim(true, &tk)?;
2994 let data = engine.stomach.data_mut();
2995 if data.pagegoal != ET::Dim::from_sp(i32::MAX) {
2996 engine.stomach.data_mut().pagegoal = d;
2997 }
2998 Ok(())
2999}
3000pub fn pagetotal_get<ET: EngineTypes>(
3001 engine: &mut EngineReferences<ET>,
3002 _tk: ET::Token,
3003) -> TeXResult<ET::Dim, ET> {
3004 Ok(engine.stomach.data_mut().pagetotal)
3005}
3006pub fn pagetotal_set<ET: EngineTypes>(
3007 engine: &mut EngineReferences<ET>,
3008 tk: ET::Token,
3009 _globally: bool,
3010) -> TeXResult<(), ET> {
3011 let d = engine.read_dim(true, &tk)?;
3012 engine.stomach.data_mut().pagetotal = d;
3013 Ok(())
3014}
3015pub fn pagestretch_get<ET: EngineTypes>(
3016 _engine: &mut EngineReferences<ET>,
3017 _tk: ET::Token,
3018) -> TeXResult<ET::Dim, ET> {
3019 Ok(ET::Dim::default()) }
3021pub fn pagestretch_set<ET: EngineTypes>(
3022 engine: &mut EngineReferences<ET>,
3023 tk: ET::Token,
3024 _globally: bool,
3025) -> TeXResult<(), ET> {
3026 let _ = engine.read_dim(true, &tk)?;
3027 Ok(())
3029}
3030pub fn pagefilstretch_get<ET: EngineTypes>(
3031 _engine: &mut EngineReferences<ET>,
3032 _tk: ET::Token,
3033) -> TeXResult<ET::Dim, ET> {
3034 Ok(ET::Dim::default()) }
3036pub fn pagefilstretch_set<ET: EngineTypes>(
3037 engine: &mut EngineReferences<ET>,
3038 tk: ET::Token,
3039 _globally: bool,
3040) -> TeXResult<(), ET> {
3041 let _ = engine.read_dim(true, &tk)?;
3042 Ok(())
3044}
3045pub fn pagefillstretch_get<ET: EngineTypes>(
3046 _engine: &mut EngineReferences<ET>,
3047 _tk: ET::Token,
3048) -> TeXResult<ET::Dim, ET> {
3049 Ok(ET::Dim::default()) }
3051pub fn pagefillstretch_set<ET: EngineTypes>(
3052 engine: &mut EngineReferences<ET>,
3053 tk: ET::Token,
3054 _globally: bool,
3055) -> TeXResult<(), ET> {
3056 let _ = engine.read_dim(true, &tk)?;
3057 Ok(())
3059}
3060pub fn pageshrink_get<ET: EngineTypes>(
3061 _engine: &mut EngineReferences<ET>,
3062 _tk: ET::Token,
3063) -> TeXResult<ET::Dim, ET> {
3064 Ok(ET::Dim::default()) }
3066pub fn pageshrink_set<ET: EngineTypes>(
3067 engine: &mut EngineReferences<ET>,
3068 tk: ET::Token,
3069 _globally: bool,
3070) -> TeXResult<(), ET> {
3071 let _ = engine.read_dim(true, &tk)?;
3072 Ok(())
3074}
3075pub fn pagefilshrink_get<ET: EngineTypes>(
3076 _engine: &mut EngineReferences<ET>,
3077 _tk: ET::Token,
3078) -> TeXResult<ET::Dim, ET> {
3079 Ok(ET::Dim::default()) }
3081pub fn pagefilshrink_set<ET: EngineTypes>(
3082 engine: &mut EngineReferences<ET>,
3083 tk: ET::Token,
3084 _globally: bool,
3085) -> TeXResult<(), ET> {
3086 let _ = engine.read_dim(true, &tk)?;
3087 Ok(())
3089}
3090pub fn pagefillshrink_get<ET: EngineTypes>(
3091 _engine: &mut EngineReferences<ET>,
3092 _tk: ET::Token,
3093) -> TeXResult<ET::Dim, ET> {
3094 Ok(ET::Dim::default()) }
3096pub fn pagefillshrink_set<ET: EngineTypes>(
3097 engine: &mut EngineReferences<ET>,
3098 tk: ET::Token,
3099 _globally: bool,
3100) -> TeXResult<(), ET> {
3101 let _ = engine.read_dim(true, &tk)?;
3102 Ok(())
3104}
3105
3106pub fn pagedepth_get<ET: EngineTypes>(
3107 engine: &mut EngineReferences<ET>,
3108 _tk: ET::Token,
3109) -> TeXResult<ET::Dim, ET> {
3110 Ok(engine.stomach.data_mut().pagedepth)
3111}
3112pub fn pagedepth_set<ET: EngineTypes>(
3113 engine: &mut EngineReferences<ET>,
3114 tk: ET::Token,
3115 _globally: bool,
3116) -> TeXResult<(), ET> {
3117 let d = engine.read_dim(true, &tk)?;
3118 engine.stomach.data_mut().pagedepth = d;
3119 Ok(())
3120}
3121
3122pub fn prevgraf_get<ET: EngineTypes>(
3123 engine: &mut EngineReferences<ET>,
3124 _tk: ET::Token,
3125) -> TeXResult<ET::Int, ET> {
3126 Ok((engine.stomach.data_mut().prevgraf as i32).into())
3127}
3128pub fn prevgraf_set<ET: EngineTypes>(
3129 engine: &mut EngineReferences<ET>,
3130 tk: ET::Token,
3131 _globally: bool,
3132) -> TeXResult<(), ET> {
3133 let d = engine.read_int(true, &tk)?.into();
3134 if d < 0 {
3135 engine.general_error(format!("Bad prevgraf ({})", d))?;
3136 } else {
3137 engine.stomach.data_mut().prevgraf = d as u16;
3138 }
3139 Ok(())
3140}
3141
3142pub fn deadcycles_get<ET: EngineTypes>(
3143 engine: &mut EngineReferences<ET>,
3144 _tk: ET::Token,
3145) -> TeXResult<ET::Int, ET> {
3146 Ok((engine.stomach.data_mut().deadcycles as i32).into())
3147}
3148pub fn deadcycles_set<ET: EngineTypes>(
3149 engine: &mut EngineReferences<ET>,
3150 tk: ET::Token,
3151 _globally: bool,
3152) -> TeXResult<(), ET> {
3153 let i = engine.read_int(true, &tk)?.into();
3154 if i >= 0 {
3155 engine.stomach.data_mut().deadcycles = i as usize
3156 }
3157 Ok(())
3158}
3159
3160pub fn vsplit<ET: EngineTypes>(
3161 engine: &mut EngineReferences<ET>,
3162 tk: ET::Token,
3163) -> TeXResult<Either<Option<TeXBox<ET>>, BoxInfo<ET>>, ET> {
3164 let idx = engine.read_register_index(false, &tk)?;
3165 let (mut info, ls, start, end) = match engine.state.get_box_register_mut(idx) {
3166 Some(TeXBox::V {
3167 info,
3168 children,
3169 start,
3170 end,
3171 }) => (
3172 info.clone_for_split(),
3173 std::mem::take(children).into_vec(),
3174 *start,
3175 *end,
3176 ),
3177 _ => {
3178 if !engine.read_keyword(b"to")? {
3179 return Err(TeXError::General(
3180 "`to` expected after \\vsplit".to_string(),
3181 ));
3182 }
3183 let _ = engine.read_dim(false, &tk)?;
3184 return Ok(either::Left(None));
3185 }
3186 };
3187 if !engine.read_keyword(b"to")? {
3188 return Err(TeXError::General(
3189 "`to` expected after \\vsplit".to_string(),
3190 ));
3191 }
3192 let target = engine.read_dim(false, &tk)?;
3193 match &mut info {
3194 VBoxInfo::VBox { scaled, .. } => {
3195 *scaled = ToOrSpread::To(target);
3196 }
3197 VBoxInfo::VTop { scaled, .. } => {
3198 *scaled = ToOrSpread::To(target);
3199 }
3200 _ => unreachable!(),
3201 }
3202 let SplitResult { first, rest, .. } = ET::Stomach::split_vertical(engine, ls, target);
3203 let ret = TeXBox::V {
3204 children: first.into(),
3205 info,
3206 start,
3207 end,
3208 };
3209 if rest.is_empty() {
3210 engine.state.set_box_register(engine.aux, idx, None, false);
3211 } else if let Some(TeXBox::V { children, .. }) = engine.state.get_box_register_mut(idx) {
3212 *children = rest.into();
3213 } else {
3214 unreachable!()
3215 }
3216 Ok(either::Left(Some(ret)))
3217}
3218
3219pub fn vadjust<ET: EngineTypes>(
3220 engine: &mut EngineReferences<ET>,
3221 tk: ET::Token,
3222) -> TeXResult<(), ET> {
3223 engine.expand_until_bgroup(true, &tk)?;
3224 engine
3225 .stomach
3226 .data_mut()
3227 .open_lists
3228 .push(NodeList::Vertical {
3229 children: vec![],
3230 tp: VerticalNodeListType::VAdjust,
3231 });
3232 engine
3233 .state
3234 .push(engine.aux, GroupType::VAdjust, engine.mouth.line_number());
3235 Ok(())
3236}
3237
3238pub fn inputlineno<ET: EngineTypes>(
3239 engine: &mut EngineReferences<ET>,
3240 _tk: ET::Token,
3241) -> TeXResult<ET::Int, ET> {
3242 Ok(ET::Int::from(engine.mouth.line_number() as i32))
3243}
3244
3245pub fn mark<ET: EngineTypes>(
3246 engine: &mut EngineReferences<ET>,
3247 tk: ET::Token,
3248) -> TeXResult<(), ET> {
3249 super::methods::do_marks(engine, 0, &tk)
3250}
3251
3252pub fn topmark<ET: EngineTypes>(
3253 engine: &mut EngineReferences<ET>,
3254 exp: &mut Vec<ET::Token>,
3255 _tk: ET::Token,
3256) -> TeXResult<(), ET> {
3257 super::methods::get_marks(engine, exp, |d| &mut d.topmarks, 0);
3258 Ok(())
3259}
3260
3261pub fn firstmark<ET: EngineTypes>(
3262 engine: &mut EngineReferences<ET>,
3263 exp: &mut Vec<ET::Token>,
3264 _tk: ET::Token,
3265) -> TeXResult<(), ET> {
3266 super::methods::get_marks(engine, exp, |d| &mut d.firstmarks, 0);
3267 Ok(())
3268}
3269
3270pub fn botmark<ET: EngineTypes>(
3271 engine: &mut EngineReferences<ET>,
3272 exp: &mut Vec<ET::Token>,
3273 _tk: ET::Token,
3274) -> TeXResult<(), ET> {
3275 super::methods::get_marks(engine, exp, |d| &mut d.botmarks, 0);
3276 Ok(())
3277}
3278
3279pub fn splitfirstmark<ET: EngineTypes>(
3280 engine: &mut EngineReferences<ET>,
3281 exp: &mut Vec<ET::Token>,
3282 _tk: ET::Token,
3283) -> TeXResult<(), ET> {
3284 super::methods::get_marks(engine, exp, |d| &mut d.splitfirstmarks, 0);
3285 Ok(())
3286}
3287
3288pub fn splitbotmark<ET: EngineTypes>(
3289 engine: &mut EngineReferences<ET>,
3290 exp: &mut Vec<ET::Token>,
3291 _tk: ET::Token,
3292) -> TeXResult<(), ET> {
3293 super::methods::get_marks(engine, exp, |d| &mut d.splitbotmarks, 0);
3294 Ok(())
3295}
3296
3297pub fn shipout<ET: EngineTypes>(
3298 engine: &mut EngineReferences<ET>,
3299 _tk: ET::Token,
3300) -> TeXResult<(), ET> {
3301 engine.stomach.data_mut().deadcycles = 0;
3302 match engine.read_box(false)? {
3303 either::Left(Some(bx)) => {
3304 engine.shipout(VNode::Box(bx))?;
3305 }
3309 either::Left(None) => (),
3310 either::Right(bi) => {
3311 let mut list = bi.open_list(engine.mouth.start_ref());
3312 let target = BoxTarget::new(|e, b| e.shipout(VNode::Box(b)));
3313 match list {
3314 NodeList::Horizontal {
3315 tp: HorizontalNodeListType::Box(_, _, ref mut t),
3316 ..
3317 } => *t = target,
3318 NodeList::Vertical {
3319 tp: VerticalNodeListType::Box(_, _, ref mut t),
3320 ..
3321 } => *t = target,
3322 _ => unreachable!(),
3323 }
3324 let data = engine.stomach.data_mut();
3325 data.open_lists.push(list);
3326 }
3327 }
3328 Ok(())
3329}
3330
3331pub fn displaylimits<ET: EngineTypes>(
3332 engine: &mut EngineReferences<ET>,
3333 _tk: ET::Token,
3334) -> TeXResult<(), ET> {
3335 let ls = engine.stomach.data_mut().open_lists.last_mut().unwrap();
3336 match ls {
3337 NodeList::Math { children, .. } => {
3338 if let Some(MathNode::Atom(MathAtom {
3339 sub: None,
3340 sup: None,
3341 nucleus: MathNucleus::Simple { ref mut limits, .. },
3342 ..
3343 })) = children.list_mut().last_mut()
3344 {
3345 *limits = None;
3346 }
3347 }
3348 _ => unreachable!(),
3349 }
3350 Ok(())
3351}
3352pub fn limits<ET: EngineTypes>(
3353 engine: &mut EngineReferences<ET>,
3354 _tk: ET::Token,
3355) -> TeXResult<(), ET> {
3356 let ls = engine.stomach.data_mut().open_lists.last_mut().unwrap();
3357 match ls {
3358 NodeList::Math { children, .. } => {
3359 if let Some(MathNode::Atom(MathAtom {
3360 sub: None,
3361 sup: None,
3362 nucleus:
3363 MathNucleus::Simple {
3364 cls: MathClass::Op,
3365 ref mut limits,
3366 ..
3367 },
3368 ..
3369 })) = children.list_mut().last_mut()
3370 {
3371 *limits = Some(true);
3372 }
3373 }
3374 _ => unreachable!(),
3375 }
3376 Ok(())
3377}
3378pub fn nolimits<ET: EngineTypes>(
3379 engine: &mut EngineReferences<ET>,
3380 _tk: ET::Token,
3381) -> TeXResult<(), ET> {
3382 let ls = engine.stomach.data_mut().open_lists.last_mut().unwrap();
3383 match ls {
3384 NodeList::Math { children, .. } => {
3385 if let Some(MathNode::Atom(MathAtom {
3386 sub: None,
3387 sup: None,
3388 nucleus:
3389 MathNucleus::Simple {
3390 cls: MathClass::Op,
3391 ref mut limits,
3392 ..
3393 },
3394 ..
3395 })) = children.list_mut().last_mut()
3396 {
3397 *limits = Some(false);
3398 }
3399 }
3400 _ => unreachable!(),
3401 }
3402 Ok(())
3403}
3404
3405pub fn underline<ET: EngineTypes>(
3406 engine: &mut EngineReferences<ET>,
3407 tk: ET::Token,
3408) -> TeXResult<(), ET> {
3409 engine.read_char_or_math_group(
3410 &tk,
3411 |_, engine, mc| {
3412 ET::Stomach::add_node_m(
3413 engine,
3414 MathNode::Atom(MathAtom {
3415 nucleus: MathNucleus::Underline(MathKernel::Char {
3416 char: mc.char,
3417 style: mc.style,
3418 }),
3419 sub: None,
3420 sup: None,
3421 }),
3422 );
3423 Ok(())
3424 },
3425 |_| {
3426 ListTarget::<ET, _>::new(|engine, children, start| {
3427 ET::Stomach::add_node_m(
3428 engine,
3429 MathNode::Atom(MathAtom {
3430 sup: None,
3431 sub: None,
3432 nucleus: MathNucleus::Underline(MathKernel::List {
3433 children: children.into(),
3434 start,
3435 end: engine.mouth.current_sourceref(),
3436 }),
3437 }),
3438 );
3439 Ok(())
3440 })
3441 },
3442 (),
3443 )
3444}
3445pub fn overline<ET: EngineTypes>(
3446 engine: &mut EngineReferences<ET>,
3447 tk: ET::Token,
3448) -> TeXResult<(), ET> {
3449 engine.read_char_or_math_group(
3450 &tk,
3451 |_, engine, mc| {
3452 ET::Stomach::add_node_m(
3453 engine,
3454 MathNode::Atom(MathAtom {
3455 nucleus: MathNucleus::Overline(MathKernel::Char {
3456 char: mc.char,
3457 style: mc.style,
3458 }),
3459 sub: None,
3460 sup: None,
3461 }),
3462 );
3463 Ok(())
3464 },
3465 |_| {
3466 ListTarget::<ET, _>::new(|engine, children, start| {
3467 ET::Stomach::add_node_m(
3468 engine,
3469 MathNode::Atom(MathAtom {
3470 sup: None,
3471 sub: None,
3472 nucleus: MathNucleus::Overline(MathKernel::List {
3473 children: children.into(),
3474 start,
3475 end: engine.mouth.current_sourceref(),
3476 }),
3477 }),
3478 );
3479 Ok(())
3480 })
3481 },
3482 (),
3483 )
3484}
3485
3486pub fn mathaccent<ET: EngineTypes>(
3487 engine: &mut EngineReferences<ET>,
3488 tk: ET::Token,
3489) -> TeXResult<(), ET> {
3490 let i = engine.read_int(false, &tk)?.into();
3491 if i < 0 || i > 32767 as i64 {
3492 return Err(TeXError::General(format!("Illegal math accent number {i}")));
3493 }
3494 let char = MathChar::from_u32(i as u32, engine.state, None);
3495 engine.read_char_or_math_group(
3496 &tk,
3497 |(char, style), engine, mc| {
3498 ET::Stomach::add_node_m(
3499 engine,
3500 MathNode::Atom(MathAtom {
3501 nucleus: MathNucleus::Accent {
3502 accent: (char, style),
3503 inner: vec![MathNode::Atom(mc.to_atom())].into(),
3504 },
3505 sub: None,
3506 sup: None,
3507 }),
3508 );
3509 Ok(())
3510 },
3511 |(char, style)| {
3512 ListTarget::<ET, _>::new(move |engine, children, _| {
3513 ET::Stomach::add_node_m(
3514 engine,
3515 MathNode::Atom(MathAtom {
3516 sup: None,
3517 sub: None,
3518 nucleus: MathNucleus::Accent {
3519 accent: (char, style),
3520 inner: children.into(),
3521 },
3522 }),
3523 );
3524 Ok(())
3525 })
3526 },
3527 (char.char, char.style),
3528 )
3529}
3530
3531pub fn radical<ET: EngineTypes>(
3532 engine: &mut EngineReferences<ET>,
3533 tk: ET::Token,
3534) -> TeXResult<(), ET> {
3535 let i = engine.read_int(false, &tk)?;
3536 if i.into() < 0 || i.into() > u32::MAX as i64 {
3537 return Err(TeXError::General(format!("Illegal radical number {i}")));
3538 }
3539 let char = Delimiter::from_int(i, engine.state).left().unwrap().small; engine.read_char_or_math_group(
3541 &tk,
3542 |(char, style), engine, mc| {
3543 ET::Stomach::add_node_m(
3544 engine,
3545 MathNode::Atom(MathAtom {
3546 nucleus: MathNucleus::Radical {
3547 rad: (char, style),
3548 inner: vec![MathNode::Atom(mc.to_atom())].into(),
3549 },
3550 sub: None,
3551 sup: None,
3552 }),
3553 );
3554 Ok(())
3555 },
3556 |(char, style)| {
3557 ListTarget::<ET, _>::new(move |engine, children, _| {
3558 ET::Stomach::add_node_m(
3559 engine,
3560 MathNode::Atom(MathAtom {
3561 sup: None,
3562 sub: None,
3563 nucleus: MathNucleus::Radical {
3564 rad: (char, style),
3565 inner: children.into(),
3566 },
3567 }),
3568 );
3569 Ok(())
3570 })
3571 },
3572 (char.char, char.style),
3573 )
3574}
3575
3576fn do_eqno<ET: EngineTypes>(
3577 engine: &mut EngineReferences<ET>,
3578 pos: EqNoPosition,
3579) -> TeXResult<(), ET> {
3580 match engine.stomach.data_mut().open_lists.last_mut() {
3581 Some(NodeList::Math {
3582 children: ch @ MathNodeList::Simple(_) | ch @ MathNodeList::Over { .. },
3583 tp: MathNodeListType::Top { .. },
3584 start,
3585 }) => {
3586 let old = std::mem::replace(
3587 ch,
3588 MathNodeList::EqNo {
3589 pos,
3590 main: vec![],
3591 eqno: vec![],
3592 },
3593 );
3594 let (children, None) = old.close(*start, engine.mouth.current_sourceref()) else {
3595 unreachable!()
3596 };
3597 let MathNodeList::EqNo { main, .. } = ch else {
3598 unreachable!()
3599 };
3600 *main = children;
3601 }
3602 _ => return Err(TeXError::General("\\eqno outside of math mode".to_string())),
3603 }
3604 Ok(())
3605}
3606
3607pub fn eqno<ET: EngineTypes>(
3608 engine: &mut EngineReferences<ET>,
3609 _tk: ET::Token,
3610) -> TeXResult<(), ET> {
3611 do_eqno(engine, EqNoPosition::Right)
3612}
3613
3614pub fn leqno<ET: EngineTypes>(
3615 engine: &mut EngineReferences<ET>,
3616 _tk: ET::Token,
3617) -> TeXResult<(), ET> {
3618 do_eqno(engine, EqNoPosition::Left)
3619}
3620
3621pub fn over<ET: EngineTypes>(
3622 engine: &mut EngineReferences<ET>,
3623 _tk: ET::Token,
3624) -> TeXResult<(), ET> {
3625 match engine.stomach.data_mut().open_lists.last_mut() {
3626 Some(NodeList::Math {
3627 children: ch @ MathNodeList::Simple(_),
3628 ..
3629 }) => {
3630 let old = std::mem::replace(
3632 ch,
3633 MathNodeList::Over {
3634 top: vec![],
3635 bottom: vec![],
3636 left: None,
3637 right: None,
3638 sep: None,
3639 },
3640 );
3641 if let MathNodeList::Over { top, .. } = ch {
3642 *top = if let MathNodeList::Simple(v) = old {
3643 v
3644 } else {
3645 unreachable!()
3646 };
3647 } else {
3648 unreachable!()
3649 }
3650 }
3651 Some(NodeList::Math { .. }) => {
3652 return Err(TeXError::General(
3653 "Incompatible list for \\over".to_string(),
3654 ))
3655 }
3656 _ => unreachable!(),
3657 }
3658 Ok(())
3659}
3660
3661pub fn overwithdelims<ET: EngineTypes>(
3662 engine: &mut EngineReferences<ET>,
3663 tk: ET::Token,
3664) -> TeXResult<(), ET> {
3665 let left = engine
3666 .read_opt_delimiter(&tk)?
3667 .map(|d| (d.small.char, d.small.style));
3668 let right = engine
3669 .read_opt_delimiter(&tk)?
3670 .map(|d| (d.small.char, d.small.style));
3671 match engine.stomach.data_mut().open_lists.last_mut() {
3672 Some(NodeList::Math {
3673 children: ch @ MathNodeList::Simple(_),
3674 ..
3675 }) => {
3676 let old = std::mem::replace(
3678 ch,
3679 MathNodeList::Over {
3680 top: vec![],
3681 bottom: vec![],
3682 left,
3683 right,
3684 sep: None,
3685 },
3686 );
3687 if let MathNodeList::Over { top, .. } = ch {
3688 *top = if let MathNodeList::Simple(v) = old {
3689 v
3690 } else {
3691 unreachable!()
3692 };
3693 } else {
3694 unreachable!()
3695 }
3696 }
3697 Some(NodeList::Math { .. }) => {
3698 return Err(TeXError::General(
3699 "Incompatible list for \\overwithdelims".to_string(),
3700 ))
3701 }
3702 _ => unreachable!(),
3703 }
3704 Ok(())
3705}
3706
3707pub fn above<ET: EngineTypes>(
3708 engine: &mut EngineReferences<ET>,
3709 tk: ET::Token,
3710) -> TeXResult<(), ET> {
3711 let sep = engine.read_dim(false, &tk)?;
3712 match engine.stomach.data_mut().open_lists.last_mut() {
3713 Some(NodeList::Math {
3714 children: ch @ MathNodeList::Simple(_),
3715 ..
3716 }) => {
3717 let old = std::mem::replace(
3719 ch,
3720 MathNodeList::Over {
3721 top: vec![],
3722 bottom: vec![],
3723 left: None,
3724 right: None,
3725 sep: Some(sep),
3726 },
3727 );
3728 if let MathNodeList::Over { top, .. } = ch {
3729 *top = if let MathNodeList::Simple(v) = old {
3730 v
3731 } else {
3732 unreachable!()
3733 };
3734 } else {
3735 unreachable!()
3736 }
3737 }
3738 Some(NodeList::Math { .. }) => {
3739 return Err(TeXError::General(
3740 "Incompatible list for \\above".to_string(),
3741 ))
3742 }
3743 _ => unreachable!(),
3744 }
3745 Ok(())
3746}
3747
3748pub fn abovewithdelims<ET: EngineTypes>(
3749 engine: &mut EngineReferences<ET>,
3750 tk: ET::Token,
3751) -> TeXResult<(), ET> {
3752 let left = engine
3753 .read_opt_delimiter(&tk)?
3754 .map(|d| (d.small.char, d.small.style));
3755 let right = engine
3756 .read_opt_delimiter(&tk)?
3757 .map(|d| (d.small.char, d.small.style));
3758 let sep = engine.read_dim(false, &tk)?;
3759 match engine.stomach.data_mut().open_lists.last_mut() {
3760 Some(NodeList::Math {
3761 children: ch @ MathNodeList::Simple(_),
3762 ..
3763 }) => {
3764 let old = std::mem::replace(
3766 ch,
3767 MathNodeList::Over {
3768 top: vec![],
3769 bottom: vec![],
3770 left,
3771 right,
3772 sep: Some(sep),
3773 },
3774 );
3775 if let MathNodeList::Over { top, .. } = ch {
3776 *top = if let MathNodeList::Simple(v) = old {
3777 v
3778 } else {
3779 unreachable!()
3780 };
3781 } else {
3782 unreachable!()
3783 }
3784 }
3785 Some(NodeList::Math { .. }) => {
3786 return Err(TeXError::General(
3787 "Incompatible list for \\abovewithdelims".to_string(),
3788 ))
3789 }
3790 _ => unreachable!(),
3791 }
3792 Ok(())
3793}
3794
3795pub fn atop<ET: EngineTypes>(
3796 engine: &mut EngineReferences<ET>,
3797 _tk: ET::Token,
3798) -> TeXResult<(), ET> {
3799 match engine.stomach.data_mut().open_lists.last_mut() {
3800 Some(NodeList::Math {
3801 children: ch @ MathNodeList::Simple(_),
3802 ..
3803 }) => {
3804 let old = std::mem::replace(
3806 ch,
3807 MathNodeList::Over {
3808 top: vec![],
3809 bottom: vec![],
3810 left: None,
3811 right: None,
3812 sep: Some(ET::Dim::default()),
3813 },
3814 );
3815 if let MathNodeList::Over { top, .. } = ch {
3816 *top = if let MathNodeList::Simple(v) = old {
3817 v
3818 } else {
3819 unreachable!()
3820 };
3821 } else {
3822 unreachable!()
3823 }
3824 }
3825 Some(NodeList::Math { .. }) => {
3826 return Err(TeXError::General(
3827 "Incompatible list for \\atop".to_string(),
3828 ))
3829 }
3830 _ => unreachable!(),
3831 }
3832 Ok(())
3833}
3834
3835pub fn atopwithdelims<ET: EngineTypes>(
3836 engine: &mut EngineReferences<ET>,
3837 tk: ET::Token,
3838) -> TeXResult<(), ET> {
3839 let left = engine
3840 .read_opt_delimiter(&tk)?
3841 .map(|d| (d.small.char, d.small.style));
3842 let right = engine
3843 .read_opt_delimiter(&tk)?
3844 .map(|d| (d.small.char, d.small.style));
3845 match engine.stomach.data_mut().open_lists.last_mut() {
3846 Some(NodeList::Math {
3847 children: ch @ MathNodeList::Simple(_),
3848 ..
3849 }) => {
3850 let old = std::mem::replace(
3852 ch,
3853 MathNodeList::Over {
3854 top: vec![],
3855 bottom: vec![],
3856 left,
3857 right,
3858 sep: Some(ET::Dim::default()),
3859 },
3860 );
3861 if let MathNodeList::Over { top, .. } = ch {
3862 *top = if let MathNodeList::Simple(v) = old {
3863 v
3864 } else {
3865 unreachable!()
3866 };
3867 } else {
3868 unreachable!()
3869 }
3870 }
3871 Some(NodeList::Math { .. }) => {
3872 return Err(TeXError::General(
3873 "Incompatible list for \\atopwithdelims".to_string(),
3874 ))
3875 }
3876 _ => unreachable!(),
3877 }
3878 Ok(())
3879}
3880
3881pub fn mathord<ET: EngineTypes>(
3882 engine: &mut EngineReferences<ET>,
3883 tk: ET::Token,
3884) -> TeXResult<(), ET> {
3885 super::methods::do_math_class(engine, Some(MathClass::Ord), &tk)
3886}
3887pub fn mathop<ET: EngineTypes>(
3888 engine: &mut EngineReferences<ET>,
3889 tk: ET::Token,
3890) -> TeXResult<(), ET> {
3891 super::methods::do_math_class(engine, Some(MathClass::Op), &tk)
3892}
3893pub fn mathbin<ET: EngineTypes>(
3894 engine: &mut EngineReferences<ET>,
3895 tk: ET::Token,
3896) -> TeXResult<(), ET> {
3897 super::methods::do_math_class(engine, Some(MathClass::Bin), &tk)
3898}
3899pub fn mathrel<ET: EngineTypes>(
3900 engine: &mut EngineReferences<ET>,
3901 tk: ET::Token,
3902) -> TeXResult<(), ET> {
3903 super::methods::do_math_class(engine, Some(MathClass::Rel), &tk)
3904}
3905pub fn mathopen<ET: EngineTypes>(
3906 engine: &mut EngineReferences<ET>,
3907 tk: ET::Token,
3908) -> TeXResult<(), ET> {
3909 super::methods::do_math_class(engine, Some(MathClass::Open), &tk)
3910}
3911pub fn mathclose<ET: EngineTypes>(
3912 engine: &mut EngineReferences<ET>,
3913 tk: ET::Token,
3914) -> TeXResult<(), ET> {
3915 super::methods::do_math_class(engine, Some(MathClass::Close), &tk)
3916}
3917pub fn mathpunct<ET: EngineTypes>(
3918 engine: &mut EngineReferences<ET>,
3919 tk: ET::Token,
3920) -> TeXResult<(), ET> {
3921 super::methods::do_math_class(engine, Some(MathClass::Punct), &tk)
3922}
3923pub fn mathinner<ET: EngineTypes>(
3924 engine: &mut EngineReferences<ET>,
3925 tk: ET::Token,
3926) -> TeXResult<(), ET> {
3927 super::methods::do_math_class(engine, None, &tk)
3928}
3929pub fn mathchoice<ET: EngineTypes>(
3930 engine: &mut EngineReferences<ET>,
3931 tk: ET::Token,
3932) -> TeXResult<(), ET> {
3933 engine.read_char_or_math_group(
3934 &tk.clone(),
3935 |tk, engine, mc| mathchoice_i(engine, vec![MathNode::Atom(mc.to_atom())].into(), tk),
3936 |tk| {
3937 ListTarget::<ET, _>::new(|engine, children, _| {
3938 mathchoice_i(engine, children.into(), tk)
3939 })
3940 },
3941 tk,
3942 )
3943}
3944type ML<ET> = Box<[MathNode<ET, UnresolvedMathFontStyle<ET>>]>;
3945pub fn mathchoice_i<ET: EngineTypes>(
3946 engine: &mut EngineReferences<ET>,
3947 d: ML<ET>,
3948 tk: ET::Token,
3949) -> TeXResult<(), ET> {
3950 engine.read_char_or_math_group(
3951 &tk.clone(),
3952 |(d, tk), engine, mc| {
3953 mathchoice_ii(engine, d, vec![MathNode::Atom(mc.to_atom())].into(), tk)
3954 },
3955 |(d, tk)| {
3956 ListTarget::<ET, _>::new(move |engine, children, _| {
3957 mathchoice_ii(engine, d, children.into(), tk)
3958 })
3959 },
3960 (d, tk),
3961 )
3962}
3963pub fn mathchoice_ii<ET: EngineTypes>(
3964 engine: &mut EngineReferences<ET>,
3965 d: ML<ET>,
3966 t: ML<ET>,
3967 tk: ET::Token,
3968) -> TeXResult<(), ET> {
3969 engine.read_char_or_math_group(
3970 &tk.clone(),
3971 |(d, t, tk), engine, mc| {
3972 mathchoice_iii(engine, d, t, vec![MathNode::Atom(mc.to_atom())].into(), tk)
3973 },
3974 |(d, t, tk)| {
3975 ListTarget::<ET, _>::new(|engine, children, _| {
3976 mathchoice_iii(engine, d, t, children.into(), tk)
3977 })
3978 },
3979 (d, t, tk),
3980 )
3981}
3982pub fn mathchoice_iii<ET: EngineTypes>(
3983 engine: &mut EngineReferences<ET>,
3984 d: ML<ET>,
3985 t: ML<ET>,
3986 s: ML<ET>,
3987 tk: ET::Token,
3988) -> TeXResult<(), ET> {
3989 engine.read_char_or_math_group(
3990 &tk,
3991 |(d, t, s), engine, mc| {
3992 ET::Stomach::add_node_m(
3993 engine,
3994 MathNode::Choice(UnresolvedMathChoice {
3995 display: d,
3996 text: t,
3997 script: s,
3998 scriptscript: vec![MathNode::Atom(mc.to_atom())].into(),
3999 }),
4000 );
4001 Ok(())
4002 },
4003 |(d, t, s)| {
4004 ListTarget::<ET, _>::new(|engine, children, _| {
4005 ET::Stomach::add_node_m(
4006 engine,
4007 MathNode::Choice(UnresolvedMathChoice {
4008 display: d,
4009 text: t,
4010 script: s,
4011 scriptscript: children.into(),
4012 }),
4013 );
4014 Ok(())
4015 })
4016 },
4017 (d, t, s),
4018 )
4019}
4020
4021pub fn displaystyle<ET: EngineTypes>(
4022 engine: &mut EngineReferences<ET>,
4023 _tk: ET::Token,
4024) -> TeXResult<(), ET> {
4025 ET::Stomach::add_node_m(engine, MathNode::Marker(UnresolvedMarkers::Display));
4026 Ok(())
4027}
4028pub fn textstyle<ET: EngineTypes>(
4029 engine: &mut EngineReferences<ET>,
4030 _tk: ET::Token,
4031) -> TeXResult<(), ET> {
4032 ET::Stomach::add_node_m(engine, MathNode::Marker(UnresolvedMarkers::Text));
4033 Ok(())
4034}
4035pub fn scriptstyle<ET: EngineTypes>(
4036 engine: &mut EngineReferences<ET>,
4037 _tk: ET::Token,
4038) -> TeXResult<(), ET> {
4039 ET::Stomach::add_node_m(engine, MathNode::Marker(UnresolvedMarkers::Script));
4040 Ok(())
4041}
4042pub fn scriptscriptstyle<ET: EngineTypes>(
4043 engine: &mut EngineReferences<ET>,
4044 _tk: ET::Token,
4045) -> TeXResult<(), ET> {
4046 ET::Stomach::add_node_m(engine, MathNode::Marker(UnresolvedMarkers::ScriptScript));
4047 Ok(())
4048}
4049
4050const PRIMITIVE_INTS: &[&str] = &[
4051 "year",
4052 "month",
4053 "day",
4054 "time",
4055 "adjdemerits",
4056 "badness",
4057 "binoppenalty",
4058 "brokenpenalty",
4059 "clubpenalty",
4060 "defaulthyphenchar",
4061 "defaultskewchar",
4062 "delimiterfactor",
4063 "displaywidowpenalty",
4064 "doublehyphendemerits",
4065 "errorcontextlines",
4066 "exhyphenpenalty",
4067 "fam",
4068 "finalhyphendemerits",
4069 "floatingpenalty",
4070 "globaldefs",
4071 "hangafter",
4072 "hbadness",
4073 "holdinginserts",
4074 "hyphenpenalty",
4075 "interlinepenalty",
4076 "language",
4077 "lefthyphenmin",
4078 "linepenalty",
4079 "looseness",
4080 "mag",
4081 "maxdeadcycles",
4082 "outputpenalty",
4083 "pausing",
4084 "postdisplaypenalty",
4085 "predisplaypenalty",
4086 "relpenalty",
4087 "righthyphenmin",
4088 "pretolerance",
4089 "showboxbreadth",
4090 "showboxdepth",
4091 "tolerance",
4092 "tracingcommands",
4093 "tracinglostchars",
4094 "tracingmacros",
4095 "tracingonline",
4096 "tracingoutput",
4097 "tracingpages",
4098 "tracingparagraphs",
4099 "tracingrestores",
4100 "tracingstats",
4101 "uchyph",
4102 "vbadness",
4103 "widowpenalty",
4104 "synctex", "insertpenalties", ];
4107
4108const PRIMITIVE_DIMS: &[&str] = &[
4109 "boxmaxdepth",
4110 "delimitershortfall",
4111 "displayindent",
4112 "displaywidth",
4113 "hangindent",
4114 "emergencystretch",
4115 "hfuzz",
4116 "hoffset",
4117 "hsize",
4118 "lineskiplimit",
4119 "maxdepth",
4120 "mathsurround",
4121 "nulldelimiterspace",
4122 "overfullrule",
4123 "parindent",
4124 "predisplaysize",
4125 "scriptspace",
4126 "splitmaxdepth",
4127 "vfuzz",
4128 "voffset",
4129 "vsize",
4130];
4131
4132const PRIMITIVE_SKIPS: &[&str] = &[
4133 "abovedisplayshortskip",
4134 "abovedisplayskip",
4135 "baselineskip",
4136 "belowdisplayshortskip",
4137 "belowdisplayskip",
4138 "leftskip",
4139 "lineskip",
4140 "parfillskip",
4141 "parskip",
4142 "rightskip",
4143 "spaceskip",
4144 "splittopskip",
4145 "tabskip",
4146 "topskip",
4147 "xspaceskip",
4148];
4149
4150const PRIMITIVE_MUSKIPS: &[&str] = &["thinmuskip", "medmuskip", "thickmuskip"];
4151
4152const PRIMITIVE_TOKS: &[&str] = &[
4153 "everypar",
4154 "everymath",
4155 "everydisplay",
4156 "everyhbox",
4157 "everyvbox",
4158 "everyjob",
4159 "everycr",
4160 "output",
4161 "errhelp",
4162];
4163
4164#[allow(unreachable_code)]
4165pub fn char_space<ET: EngineTypes>(
4166 engine: &mut EngineReferences<ET>,
4167 _tk: ET::Token,
4168) -> TeXResult<(), ET> {
4169 crate::add_node!(ET::Stomach;engine,unreachable!(), HNode::Space, MathNode::Space);
4170 Ok(())
4171}
4172pub fn char_slash<ET: EngineTypes>(
4173 _engine: &mut EngineReferences<ET>,
4174 _tk: ET::Token,
4175) -> TeXResult<(), ET> {
4176 Ok(())
4178}
4179pub fn char_dash<ET: EngineTypes>(
4180 _engine: &mut EngineReferences<ET>,
4181 _tk: ET::Token,
4182) -> TeXResult<(), ET> {
4183 Ok(())
4185}
4186
4187pub fn end_template<ET: EngineTypes>(
4188 engine: &mut EngineReferences<ET>,
4189 _tk: ET::Token,
4190) -> TeXResult<(), ET> {
4191 match engine.gullet.get_align_data() {
4192 None => unreachable!(),
4193 Some(data) => {
4194 data.omit = false;
4195 let skip = data.columns[data.currindex].tabskip;
4196 match engine.stomach.data_mut().open_lists.last_mut() {
4197 Some(NodeList::Horizontal { children, .. }) => {
4198 children.push(HNode::HSkip(skip));
4199 }
4200 Some(NodeList::Vertical { children, .. }) => {
4201 children.push(VNode::VSkip(skip));
4202 }
4203 _ => unreachable!(),
4204 }
4205 data.currindex += 1;
4206 if data.columns.len() <= data.currindex {
4207 if let Some(i) = data.repeat_index {
4208 data.currindex = i
4209 }
4210 }
4211 if data.span {
4212 match engine.stomach.data_mut().open_lists.last_mut() {
4213 Some(NodeList::Horizontal {
4214 tp: HorizontalNodeListType::HAlignCell(_, ref mut span),
4215 ..
4216 })
4217 | Some(NodeList::Vertical {
4218 tp: VerticalNodeListType::VAlignCell(_, ref mut span),
4219 ..
4220 }) => {
4221 *span += 1;
4222 }
4223 _ => unreachable!(),
4224 }
4225 } else {
4226 super::methods::pop_align_cell(
4227 engine.state,
4228 engine.aux,
4229 engine.stomach,
4230 engine.mouth,
4231 data.inner_mode,
4232 );
4233 }
4234 let mode = data.inner_mode;
4235 crate::expand_loop!(engine,token,
4236 ResolvedToken::Tk{code:CommandCode::EndGroup,..} |
4237 ResolvedToken::Cmd(Some(TeXCommand::Char {code:CommandCode::EndGroup,..})) => {
4238 return Err(TeXError::General("Unexpected end group character in alignment".to_string()))
4239 }
4240 ResolvedToken::Tk{code:CommandCode::Space,..} => (),
4241 ResolvedToken::Cmd(Some(TeXCommand::Primitive {name,..})) if *name == PRIMITIVES.omit => {
4244 engine.gullet.get_align_data().unwrap().omit = true;
4245 super::methods::open_align_cell(engine,mode);
4246 return Ok(())
4247 },
4248 ResolvedToken::Tk{..} | ResolvedToken::Cmd(_) => {
4249 engine.requeue(token)?;
4250 super::methods::open_align_cell(engine,mode);
4251 return Ok(())
4252 }
4253 );
4254 super::methods::open_align_cell(engine, mode);
4255 Ok(())
4256 }
4257 }
4258}
4259
4260pub fn end_template_row<ET: EngineTypes>(
4261 engine: &mut EngineReferences<ET>,
4262 _tk: ET::Token,
4263) -> TeXResult<(), ET> {
4264 match engine.gullet.get_align_data() {
4265 None => unreachable!(),
4266 Some(data) => {
4267 data.omit = false;
4268 let skip = data.columns[data.currindex].tabskip;
4269 match engine.stomach.data_mut().open_lists.last_mut() {
4270 Some(NodeList::Horizontal { children, .. }) => {
4271 children.push(HNode::HSkip(skip));
4272 }
4273 Some(NodeList::Vertical { children, .. }) => {
4274 children.push(VNode::VSkip(skip));
4275 }
4276 _ => unreachable!(),
4277 }
4278 let mode = data.inner_mode;
4279 super::methods::pop_align_cell(
4280 engine.state,
4281 engine.aux,
4282 engine.stomach,
4283 engine.mouth,
4284 mode,
4285 );
4286 super::methods::pop_align_row::<ET>(engine.stomach, engine.mouth, mode);
4287 super::methods::start_align_row(engine, mode)
4288 }
4289 }
4290}
4291
4292pub fn register_tex_primitives<E: TeXEngine>(engine: &mut E) {
4293 register_int(engine, "catcode", catcode_get, Some(catcode_set));
4294 register_int(engine, "hyphenchar", hyphenchar_get, Some(hyphenchar_set));
4295 register_int(engine, "skewchar", skewchar_get, Some(skewchar_set));
4296 register_int(engine, "sfcode", sfcode_get, Some(sfcode_set));
4297 register_int(engine, "lccode", lccode_get, Some(lccode_set));
4298 register_int(engine, "uccode", uccode_get, Some(uccode_set));
4299 register_int(engine, "mathcode", mathcode_get, Some(mathcode_set));
4300 register_int(engine, "delcode", delcode_get, Some(delcode_set));
4301 register_int(engine, "count", count_get, Some(count_set));
4302 register_int(
4303 engine,
4304 "endlinechar",
4305 endlinechar_get,
4306 Some(endlinechar_set),
4307 );
4308 register_int(engine, "escapechar", escapechar_get, Some(escapechar_set));
4309 register_int(
4310 engine,
4311 "newlinechar",
4312 newlinechar_get,
4313 Some(newlinechar_set),
4314 );
4315 register_int(engine, "inputlineno", inputlineno, None);
4316 register_int(
4317 engine,
4318 "spacefactor",
4319 spacefactor_get,
4320 Some(spacefactor_set),
4321 );
4322 register_int(engine, "lastpenalty", lastpenalty, None);
4323 register_int(engine, "parshape", parshape_get, Some(parshape_set));
4324 register_int(engine, "deadcycles", deadcycles_get, Some(deadcycles_set));
4325 register_int(engine, "prevgraf", prevgraf_get, Some(prevgraf_set));
4326
4327 register_dim(engine, "fontdimen", fontdimen_get, Some(fontdimen_set));
4328 register_dim(engine, "dimen", dimen_get, Some(dimen_set));
4329 register_dim(engine, "prevdepth", prevdepth_get, Some(prevdepth_set));
4330 register_dim(engine, "dp", dp_get, Some(dp_set));
4331 register_dim(engine, "ht", ht_get, Some(ht_set));
4332 register_dim(engine, "wd", wd_get, Some(wd_set));
4333 register_dim(engine, "lastkern", lastkern, None);
4334 register_dim(engine, "pagegoal", pagegoal_get, Some(pagegoal_set));
4335 register_dim(engine, "pagetotal", pagetotal_get, Some(pagegoal_set));
4336 register_dim(
4337 engine,
4338 "pagestretch",
4339 pagestretch_get,
4340 Some(pagestretch_set),
4341 );
4342 register_dim(
4343 engine,
4344 "pagefilstretch",
4345 pagefilstretch_get,
4346 Some(pagefilstretch_set),
4347 );
4348 register_dim(
4349 engine,
4350 "pagefillstretch",
4351 pagefillstretch_get,
4352 Some(pagefillstretch_set),
4353 );
4354 register_dim(engine, "pageshrink", pageshrink_get, Some(pageshrink_set));
4355 register_dim(
4356 engine,
4357 "pagefilshrink",
4358 pagefilshrink_get,
4359 Some(pagefilshrink_set),
4360 );
4361 register_dim(
4362 engine,
4363 "pagefillshrink",
4364 pagefillshrink_get,
4365 Some(pagefillshrink_set),
4366 );
4367 register_dim(engine, "pagedepth", pagedepth_get, Some(pagedepth_set));
4368
4369 register_skip(engine, "skip", skip_get, Some(skip_set));
4370 register_skip(engine, "lastskip", lastskip, None);
4371
4372 register_muskip(engine, "muskip", muskip_get, Some(muskip_set));
4373
4374 register_font(engine, "font", font_get, Some(font_set));
4375 register_font(engine, "textfont", textfont_get, Some(textfont_set));
4376 register_font(engine, "scriptfont", scriptfont_get, Some(scriptfont_set));
4377 register_font(
4378 engine,
4379 "scriptscriptfont",
4380 scriptscriptfont_get,
4381 Some(scriptscriptfont_set),
4382 );
4383
4384 register_assignment(engine, "advance", advance);
4385 register_assignment(engine, "chardef", chardef);
4386 register_assignment(engine, "countdef", countdef);
4387 register_assignment(engine, "dimendef", dimendef);
4388 register_assignment(engine, "skipdef", skipdef);
4389 register_assignment(engine, "muskipdef", muskipdef);
4390 register_assignment(engine, "toksdef", toksdef);
4391 register_assignment(engine, "def", |e, cmd, g| {
4392 def(e, cmd, false, false, false, g)
4393 });
4394 register_assignment(engine, "divide", divide);
4395 register_assignment(engine, "edef", |e, cmd, g| {
4396 edef(e, cmd, false, false, false, g)
4397 });
4398 register_assignment(engine, "xdef", |e, cmd, g| {
4399 xdef(e, cmd, false, false, false, g)
4400 });
4401 register_assignment(engine, "gdef", |e, cmd, g| {
4402 gdef(e, cmd, false, false, false, g)
4403 });
4404 register_assignment(engine, "outer", |e, cmd, g| {
4405 outer(e, cmd, false, false, false, g)
4406 });
4407 register_assignment(engine, "long", |e, cmd, g| {
4408 long(e, cmd, false, false, false, g)
4409 });
4410 register_assignment(engine, "global", |e, cmd, g| {
4411 global(e, cmd, false, false, false, g)
4412 });
4413 register_assignment(engine, "let", let_);
4414 register_assignment(engine, "futurelet", futurelet);
4415 register_assignment(engine, "mathchardef", mathchardef);
4416 register_assignment(engine, "multiply", multiply);
4417 register_assignment(engine, "read", read);
4418 register_assignment(engine, "setbox", setbox);
4419 register_assignment(engine, "toks", toks);
4420
4421 register_simple_expandable(engine, "csname", csname);
4422 register_simple_expandable(engine, "else", r#else);
4423 register_simple_expandable(engine, "endinput", endinput);
4424 register_simple_expandable(engine, "or", or);
4425 register_simple_expandable(engine, "expandafter", expandafter);
4426 register_simple_expandable(engine, "fi", fi);
4427 register_simple_expandable(engine, "input", input);
4428 register_simple_expandable(engine, "noexpand", noexpand);
4429
4430 register_expandable(engine, "jobname", jobname);
4431 register_expandable(engine, "fontname", fontname);
4432 register_expandable(engine, "meaning", meaning);
4433 register_expandable(engine, "number", number);
4434 register_expandable(engine, "romannumeral", romannumeral);
4435 register_expandable(engine, "string", string);
4436 register_expandable(engine, "the", the);
4437 register_expandable(engine, "topmark", topmark);
4438 register_expandable(engine, "firstmark", firstmark);
4439 register_expandable(engine, "botmark", botmark);
4440 register_expandable(engine, "splitfirstmark", splitfirstmark);
4441 register_expandable(engine, "splitbotmark", splitbotmark);
4442
4443 register_conditional(engine, "if", r#if);
4444 register_conditional(engine, "ifcase", ifcase);
4445 register_conditional(engine, "ifcat", ifcat);
4446 register_conditional(engine, "ifdim", ifdim);
4447 register_conditional(engine, "ifeof", ifeof);
4448 register_conditional(engine, "ifhbox", ifhbox);
4449 register_conditional(engine, "ifhmode", ifhmode);
4450 register_conditional(engine, "ifinner", ifinner);
4451 register_conditional(engine, "ifmmode", ifmmode);
4452 register_conditional(engine, "ifnum", ifnum);
4453 register_conditional(engine, "ifodd", ifodd);
4454 register_conditional(engine, "ifx", ifx);
4455 register_conditional(engine, "iftrue", iftrue);
4456 register_conditional(engine, "iffalse", iffalse);
4457 register_conditional(engine, "ifvbox", ifvbox);
4458 register_conditional(engine, "ifvmode", ifvmode);
4459 register_conditional(engine, "ifvoid", ifvoid);
4460
4461 register_unexpandable(
4462 engine,
4463 "afterassignment",
4464 CommandScope::Any,
4465 afterassignment,
4466 );
4467 register_unexpandable(engine, "aftergroup", CommandScope::Any, aftergroup);
4468 register_unexpandable(engine, "begingroup", CommandScope::Any, begingroup);
4469 register_unexpandable(engine, "closein", CommandScope::Any, closein);
4470 register_unexpandable(
4471 engine,
4472 "char",
4473 CommandScope::SwitchesToHorizontalOrMath,
4474 r#char,
4475 );
4476 register_unexpandable(engine, "discretionary", CommandScope::Any, discretionary);
4477 register_unexpandable(engine, "dump", CommandScope::Any, |_, _| Ok(()));
4478 register_unexpandable(engine, "endcsname", CommandScope::Any, endcsname);
4479 register_unexpandable(engine, "endgroup", CommandScope::Any, endgroup);
4480 register_unexpandable(engine, "end", CommandScope::Any, end);
4481 register_unexpandable(engine, "errorstopmode", CommandScope::Any, errorstopmode);
4482 register_unexpandable(engine, "halign", CommandScope::Any, halign);
4483 register_unexpandable(engine, "valign", CommandScope::SwitchesToHorizontal, valign);
4484 register_unexpandable(engine, "hyphenation", CommandScope::Any, |e, t| {
4485 e.skip_argument(&t)
4486 });
4487 register_unexpandable(engine, "ignorespaces", CommandScope::Any, ignorespaces);
4488 register_unexpandable(engine, "insert", CommandScope::Any, insert);
4489 register_unexpandable(engine, "immediate", CommandScope::Any, immediate);
4490 register_unexpandable(engine, "lowercase", CommandScope::Any, lowercase);
4491 register_unexpandable(engine, "uppercase", CommandScope::Any, uppercase);
4492 register_unexpandable(engine, "leaders", CommandScope::Any, leaders);
4493 register_unexpandable(engine, "xleaders", CommandScope::Any, xleaders);
4494 register_unexpandable(engine, "cleaders", CommandScope::Any, cleaders);
4495 register_unexpandable(engine, "message", CommandScope::Any, message);
4496 register_unexpandable(engine, "errmessage", CommandScope::Any, errmessage);
4497 register_unexpandable(engine, "noindent", CommandScope::Any, noindent);
4498 register_unexpandable(engine, "openin", CommandScope::Any, openin);
4499 register_unexpandable(engine, "par", CommandScope::Any, par);
4500 register_unexpandable(
4501 engine,
4502 "unhbox",
4503 CommandScope::SwitchesToHorizontalOrMath,
4504 unhbox,
4505 );
4506 register_unexpandable(engine, "unvbox", CommandScope::SwitchesToVertical, unvbox);
4507 register_unexpandable(
4508 engine,
4509 "unhcopy",
4510 CommandScope::SwitchesToHorizontalOrMath,
4511 unhcopy,
4512 );
4513 register_unexpandable(engine, "unvcopy", CommandScope::SwitchesToVertical, unvcopy);
4514 register_unexpandable(engine, "unskip", CommandScope::Any, unskip);
4515 register_unexpandable(engine, "unkern", CommandScope::Any, unkern);
4516 register_unexpandable(engine, "unpenalty", CommandScope::Any, unpenalty);
4517 register_unexpandable(
4518 engine,
4519 "moveleft",
4520 CommandScope::SwitchesToVertical,
4521 moveleft,
4522 );
4523 register_unexpandable(
4524 engine,
4525 "moveright",
4526 CommandScope::SwitchesToVertical,
4527 moveright,
4528 );
4529 register_unexpandable(
4530 engine,
4531 "raise",
4532 CommandScope::SwitchesToHorizontalOrMath,
4533 raise,
4534 );
4535 register_unexpandable(
4536 engine,
4537 "lower",
4538 CommandScope::SwitchesToHorizontalOrMath,
4539 lower,
4540 );
4541 register_unexpandable(engine, "shipout", CommandScope::Any, shipout);
4542 register_unexpandable(engine, "patterns", CommandScope::Any, |e, t| {
4543 e.skip_argument(&t)
4544 });
4545 register_unexpandable(
4546 engine,
4547 "vadjust",
4548 CommandScope::SwitchesToHorizontalOrMath,
4549 vadjust,
4550 );
4551 {
4552 let refs = engine.get_engine_refs();
4553 let relax = refs.aux.memory.cs_interner_mut().cs_from_str("relax");
4554 let nullfont = refs.aux.memory.cs_interner_mut().cs_from_str("nullfont");
4555 refs.state.set_command(
4556 refs.aux,
4557 relax,
4558 Some(TeXCommand::Primitive {
4559 name: PRIMITIVES.relax,
4560 cmd: PrimitiveCommand::Relax,
4561 }),
4562 true,
4563 );
4564 refs.state.set_command(
4565 refs.aux,
4566 nullfont,
4567 Some(TeXCommand::Font(refs.fontsystem.null())),
4568 true,
4569 )
4570 }
4571 register_unexpandable(engine, "mark", CommandScope::Any, mark);
4572 register_unexpandable(engine, "/", CommandScope::Any, char_slash);
4573 register_unexpandable(engine, "-", CommandScope::Any, char_dash);
4574 register_unexpandable(engine, "showlists", CommandScope::Any, |_, _| Ok(())); register_unexpandable(engine, "crcr", CommandScope::Any, |_, _| Ok(()));
4576 register_unexpandable(engine, "cr", CommandScope::Any, |_, _| {
4577 Err(TeXError::General("Unexpected \\cr".to_string()))
4578 });
4579 register_unexpandable(engine, END_TEMPLATE, CommandScope::Any, end_template);
4580 register_unexpandable(
4581 engine,
4582 END_TEMPLATE_ROW,
4583 CommandScope::Any,
4584 end_template_row,
4585 );
4586
4587 register_unexpandable(engine, "delimiter", CommandScope::MathOnly, delimiter);
4588 register_unexpandable(engine, "mskip", CommandScope::MathOnly, mskip);
4589 register_unexpandable(engine, "mkern", CommandScope::MathOnly, mkern);
4590 register_unexpandable(engine, "mathchar", CommandScope::MathOnly, mathchar);
4591 register_unexpandable(engine, "left", CommandScope::MathOnly, left);
4592 register_unexpandable(engine, "right", CommandScope::MathOnly, right);
4593 register_unexpandable(
4594 engine,
4595 "displaylimits",
4596 CommandScope::MathOnly,
4597 displaylimits,
4598 );
4599 register_unexpandable(engine, "limits", CommandScope::MathOnly, limits);
4600 register_unexpandable(engine, "nolimits", CommandScope::MathOnly, nolimits);
4601 register_unexpandable(engine, "penalty", CommandScope::Any, penalty);
4602 register_unexpandable(engine, "kern", CommandScope::Any, kern);
4603 register_unexpandable(
4604 engine,
4605 "vrule",
4606 CommandScope::SwitchesToHorizontalOrMath,
4607 vrule,
4608 );
4609 register_unexpandable(engine, "vskip", CommandScope::SwitchesToVertical, vskip);
4610 register_unexpandable(engine, "vfil", CommandScope::SwitchesToVertical, vfil);
4611 register_unexpandable(engine, "vfill", CommandScope::SwitchesToVertical, vfill);
4612 register_unexpandable(engine, "vfilneg", CommandScope::SwitchesToVertical, vfilneg);
4613 register_unexpandable(engine, "vss", CommandScope::SwitchesToVertical, vss);
4614 register_unexpandable(engine, "hrule", CommandScope::SwitchesToVertical, hrule);
4615 register_unexpandable(
4616 engine,
4617 "hskip",
4618 CommandScope::SwitchesToHorizontalOrMath,
4619 hskip,
4620 );
4621 register_unexpandable(
4622 engine,
4623 "hfil",
4624 CommandScope::SwitchesToHorizontalOrMath,
4625 hfil,
4626 );
4627 register_unexpandable(
4628 engine,
4629 "hfill",
4630 CommandScope::SwitchesToHorizontalOrMath,
4631 hfill,
4632 );
4633 register_unexpandable(
4634 engine,
4635 "hfilneg",
4636 CommandScope::SwitchesToHorizontalOrMath,
4637 hfilneg,
4638 );
4639 register_unexpandable(engine, "hss", CommandScope::SwitchesToHorizontalOrMath, hss);
4640 register_unexpandable(engine, "indent", CommandScope::SwitchesToHorizontal, indent);
4641 register_unexpandable(
4642 engine,
4643 " ",
4644 CommandScope::SwitchesToHorizontalOrMath,
4645 char_space,
4646 );
4647 register_unexpandable(engine, "vcenter", CommandScope::MathOnly, vcenter);
4648 register_unexpandable(engine, "accent", CommandScope::SwitchesToHorizontal, accent);
4649
4650 register_unexpandable(engine, "mathord", CommandScope::MathOnly, mathord);
4651 register_unexpandable(engine, "mathop", CommandScope::MathOnly, mathop);
4652 register_unexpandable(engine, "mathbin", CommandScope::MathOnly, mathbin);
4653 register_unexpandable(engine, "mathrel", CommandScope::MathOnly, mathrel);
4654 register_unexpandable(engine, "mathopen", CommandScope::MathOnly, mathopen);
4655 register_unexpandable(engine, "mathclose", CommandScope::MathOnly, mathclose);
4656 register_unexpandable(engine, "mathpunct", CommandScope::MathOnly, mathpunct);
4657 register_unexpandable(engine, "mathinner", CommandScope::MathOnly, mathinner);
4658 register_unexpandable(engine, "mathchoice", CommandScope::MathOnly, mathchoice);
4659 register_unexpandable(engine, "underline", CommandScope::MathOnly, underline);
4660 register_unexpandable(engine, "overline", CommandScope::MathOnly, overline);
4661 register_unexpandable(engine, "mathaccent", CommandScope::MathOnly, mathaccent);
4662 register_unexpandable(engine, "radical", CommandScope::MathOnly, radical);
4663 register_unexpandable(engine, "eqno", CommandScope::MathOnly, eqno);
4664 register_unexpandable(engine, "leqno", CommandScope::MathOnly, leqno);
4665
4666 register_unexpandable(engine, "over", CommandScope::MathOnly, over);
4667 register_unexpandable(
4668 engine,
4669 "overwithdelims",
4670 CommandScope::MathOnly,
4671 overwithdelims,
4672 );
4673 register_unexpandable(engine, "above", CommandScope::MathOnly, above);
4674 register_unexpandable(
4675 engine,
4676 "abovewithdelims",
4677 CommandScope::MathOnly,
4678 abovewithdelims,
4679 );
4680 register_unexpandable(engine, "atop", CommandScope::MathOnly, atop);
4681 register_unexpandable(
4682 engine,
4683 "atopwithdelims",
4684 CommandScope::MathOnly,
4685 atopwithdelims,
4686 );
4687
4688 register_unexpandable(engine, "displaystyle", CommandScope::MathOnly, displaystyle);
4689 register_unexpandable(engine, "textstyle", CommandScope::MathOnly, textstyle);
4690 register_unexpandable(engine, "scriptstyle", CommandScope::MathOnly, scriptstyle);
4691 register_unexpandable(
4692 engine,
4693 "scriptscriptstyle",
4694 CommandScope::MathOnly,
4695 scriptscriptstyle,
4696 );
4697
4698 register_unexpandable(engine, "nonscript", CommandScope::MathOnly, |_, _| Ok(())); register_whatsit(engine, "closeout", closeout, closeout_immediate, None);
4701 register_whatsit(engine, "openout", openout, openout_immediate, None);
4702 register_whatsit(engine, "write", write, write_immediate, None);
4703
4704 register_box(engine, "hbox", hbox);
4705 register_box(engine, "vbox", vbox);
4706 register_box(engine, "vtop", vtop);
4707 register_box(engine, "box", box_);
4708 register_box(engine, "copy", copy);
4709 register_box(engine, "lastbox", lastbox);
4710 register_box(engine, "vsplit", vsplit);
4711
4712 cmstodos!(engine, noalign, omit, span);
4713
4714 cmtodos!(
4715 engine,
4716 scrollmode,
4717 nonstopmode,
4718 batchmode,
4719 show,
4720 showbox,
4721 showthe,
4722 noboundary,
4723 setlanguage,
4724 bye,
4725 italiccorr
4726 );
4727
4728 register_primitive_int(engine, PRIMITIVE_INTS);
4729 register_primitive_dim(engine, PRIMITIVE_DIMS);
4730 register_primitive_skip(engine, PRIMITIVE_SKIPS);
4731 register_primitive_muskip(engine, PRIMITIVE_MUSKIPS);
4732 register_primitive_toks(engine, PRIMITIVE_TOKS);
4733
4734 register_unexpandable(engine, "special", CommandScope::Any, |e, t| {
4735 e.skip_argument(&t)
4736 });
4737}