1use super::primitives::*;
2use crate::commands::{
3 CharOrPrimitive, CommandScope, Macro, MacroSignature, PrimitiveCommand, ResolvedToken,
4 TeXCommand,
5};
6use crate::engine::filesystem::FileSystem;
7use crate::engine::fontsystem::Font;
8use crate::engine::gullet::methods::NumContinuation;
9use crate::engine::gullet::Gullet;
10use crate::engine::mouth::Mouth;
11use crate::engine::state::{GroupType, State};
12use crate::engine::stomach::Stomach;
13use crate::engine::{EngineAux, EngineReferences, EngineTypes, TeXEngine};
14use crate::tex::catcodes::{CategoryCode, CommandCode};
15use crate::tex::characters::Character;
16use crate::tex::characters::CharacterMap;
17use crate::tex::nodes::math::MathAtom;
18use crate::tex::nodes::math::{MathNode, MathNucleus};
19use crate::tex::nodes::{NodeList, NodeTrait};
20use crate::tex::numerics::{MuSkip, NumSet, Numeric, Skip, StretchShrink};
21use crate::tex::tokens::control_sequences::{CSHandler, ResolvedCSName};
22use crate::tex::tokens::token_lists::{CharWrite, Otherize};
23use crate::tex::tokens::{StandardToken, Token};
24use crate::utils::errors::{TeXError, TeXResult};
25use either::Either;
26
27#[allow(non_snake_case)]
28pub fn eTeXversion<ET: EngineTypes>(
29 _engine: &mut EngineReferences<ET>,
30 _tk: ET::Token,
31) -> TeXResult<ET::Int, ET> {
32 Ok(2.into())
33}
34#[allow(non_snake_case)]
35pub fn eTeXrevision<ET: EngineTypes>(
36 _engine: &mut EngineReferences<ET>,
37 exp: &mut Vec<ET::Token>,
38 _tk: ET::Token,
39) -> TeXResult<(), ET> {
40 exp.push(ET::Token::from_char_cat(b'.'.into(), CommandCode::Other));
41 exp.push(ET::Token::from_char_cat(b'6'.into(), CommandCode::Other));
42 Ok(())
43}
44
45fn expr_inner<ET: EngineTypes, R: Numeric<<ET::Num as NumSet>::Int>>(
46 engine: &mut EngineReferences<ET>,
47 byte: fn(&mut EngineReferences<ET>, bool, u8) -> TeXResult<R, ET>,
48 cmd: fn(&mut EngineReferences<ET>, bool, TeXCommand<ET>, ET::Token) -> TeXResult<R, ET>,
49 tk: &ET::Token,
50) -> TeXResult<R, ET> {
51 let NumContinuation { is_negative, next } =
52 crate::engine::gullet::methods::read_numeric(engine, false, tk)?;
53 match next {
54 Either::Left(b) => {
55 if b == b'(' {
56 let (int, ret) = expr_loop(engine, byte, cmd, tk)?;
57 match ret {
58 Some(ret) => match ret.to_enum() {
59 StandardToken::Character(char, CommandCode::Other)
60 if matches!(char.try_into(), Ok(b')')) =>
61 {
62 if is_negative {
63 Ok(-int)
64 } else {
65 Ok(int)
66 }
67 }
68 _ => Err(TeXError::General(
69 "Closing Parenthesis expected\nTODO: Better error message".to_string(),
70 )),
71 },
72 _ => Err(TeXError::General(
73 "Closing Parenthesis expected\nTODO: Better error message".to_string(),
74 )),
75 }
76 } else {
77 byte(engine, is_negative, b)
78 }
79 }
80 Either::Right((c, token)) => cmd(engine, is_negative, c, token),
81 }
82}
83
84struct Summand<ET: EngineTypes, R: Numeric<ET::Int>> {
85 base: R,
86 times: Vec<ET::Int>,
87 div: Vec<ET::Int>,
88}
89impl<ET: EngineTypes, R: Numeric<ET::Int>> Summand<ET, R> {
90 fn new(r: R) -> Self {
91 Self {
92 base: r,
93 times: vec![],
94 div: vec![],
95 }
96 }
97 fn resolve(self) -> R {
98 let times = if self.times.is_empty() {
99 ET::Int::from(1)
100 } else {
101 self.times.into_iter().reduce(|a, b| a * b).unwrap()
102 };
103 let div = if self.div.is_empty() {
104 ET::Int::from(1)
105 } else {
106 self.div.into_iter().reduce(|a, b| a * b).unwrap()
107 };
108 self.base.scale(times, div)
109 }
110 fn mult(&mut self, i: ET::Int) {
111 self.times.push(i)
112 }
113 fn div(&mut self, i: ET::Int) {
114 self.div.push(i)
115 }
116}
117impl<ET: EngineTypes, R: Numeric<ET::Int>> std::fmt::Display for Summand<ET, R> {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 write!(f, "({}", self.base)?;
120 for x in self.times.iter() {
121 write!(f, " *{}", x)?;
122 }
123 for x in self.div.iter() {
124 write!(f, " /{}", x)?;
125 }
126 write!(f, ")")
127 }
128}
129
130fn expr_loop<ET: EngineTypes, R: Numeric<<ET::Num as NumSet>::Int>>(
131 engine: &mut EngineReferences<ET>,
132 byte: fn(&mut EngineReferences<ET>, bool, u8) -> TeXResult<R, ET>,
133 cmd: fn(&mut EngineReferences<ET>, bool, TeXCommand<ET>, ET::Token) -> TeXResult<R, ET>,
134 tk: &ET::Token,
135) -> TeXResult<(R, Option<ET::Token>), ET> {
136 let mut prev: Option<R> = None;
137 let mut curr = Summand::<ET, R>::new(expr_inner(engine, byte, cmd, tk)?);
138 loop {
139 match engine.read_chars(b"+-*/")? {
140 Either::Right(r) => {
141 match prev {
142 Some(p) => return Ok((p + curr.resolve(), r)),
143 _ => return Ok((curr.resolve(), r)),
144 }
145 }
147 Either::Left(b'+') => {
148 let old =
149 std::mem::replace(&mut curr, Summand::new(expr_inner(engine, byte, cmd, tk)?))
150 .resolve();
151 prev = match prev {
152 Some(s) => Some(s + old),
153 None => Some(old),
154 }
155 }
156 Either::Left(b'-') => {
157 let old =
158 std::mem::replace(&mut curr, Summand::new(-expr_inner(engine, byte, cmd, tk)?))
159 .resolve();
160 prev = match prev {
161 Some(s) => Some(s + old),
162 None => Some(old),
163 }
164 }
165 Either::Left(b'*') => curr.mult(expr_inner(
166 engine,
167 crate::engine::gullet::methods::read_int_byte,
168 crate::engine::gullet::methods::read_int_command,
169 tk,
170 )?),
171 Either::Left(b'/') => curr.div(expr_inner(
172 engine,
173 crate::engine::gullet::methods::read_int_byte,
174 crate::engine::gullet::methods::read_int_command,
175 tk,
176 )?),
177 Either::Left(_) => unreachable!(),
178 }
179 }
180}
181
182pub fn currentgrouplevel<ET: EngineTypes>(
183 engine: &mut EngineReferences<ET>,
184 _tk: ET::Token,
185) -> TeXResult<ET::Int, ET> {
186 Ok((engine.state.get_group_level() as i32).into())
187}
188pub fn currentgrouptype<ET: EngineTypes>(
209 engine: &mut EngineReferences<ET>,
210 _tk: ET::Token,
211) -> TeXResult<ET::Int, ET> {
212 Ok(match engine.state.get_group_type() {
213 None => ET::Int::default(),
214 Some(gt) => ET::Int::from(gt.to_byte() as i32),
215 })
216}
217
218pub fn detokenize<ET: EngineTypes>(
219 engine: &mut EngineReferences<ET>,
220 exp: &mut Vec<ET::Token>,
221 tk: ET::Token,
222) -> TeXResult<(), ET> {
223 engine.expand_until_bgroup(false, &tk)?;
224 let mut f = |t| exp.push(t);
225 let escapechar = engine.state.get_escape_char();
226 let g = |a: &mut EngineAux<ET>, st: &<ET as EngineTypes>::State, t: ET::Token, f: &mut _| {
227 let mut tokenizer = Otherize::new(f);
228 match t.to_enum() {
229 StandardToken::Character(c, _) => tokenizer.push_char(c),
230 StandardToken::ControlSequence(cs) => tokenizer.push_cs(
231 cs,
232 a.memory.cs_interner(),
233 st.get_catcode_scheme(),
234 escapechar,
235 ),
236 StandardToken::Primitive(id) => {
237 tokenizer.push_cs(
238 a.memory.cs_interner_mut().cs_from_str("pdfprimitive"),
239 a.memory.cs_interner(),
240 st.get_catcode_scheme(),
241 escapechar,
242 );
243 let name = a
244 .memory
245 .cs_interner_mut()
246 .cs_from_str(&id.display::<ET::Char>(None).to_string());
247 tokenizer.push_cs(
248 name,
249 a.memory.cs_interner(),
250 st.get_catcode_scheme(),
251 escapechar,
252 );
253 }
254 }
255 };
256 engine.read_until_endgroup(&tk, |a, st, t| {
257 match t.command_code() {
258 CommandCode::Space => f(t),
259 CommandCode::Parameter => {
260 g(a, st, t.clone(), &mut f);
261 g(a, st, t, &mut f)
262 }
263 _ => g(a, st, t, &mut f),
264 }
265 Ok(())
266 })?;
267 Ok(())
268}
269
270pub fn expanded<ET: EngineTypes>(
271 engine: &mut EngineReferences<ET>,
272 exp: &mut Vec<ET::Token>,
273 tk: ET::Token,
274) -> TeXResult<(), ET> {
275 if engine.need_next(false, &tk)?.command_code() == CommandCode::BeginGroup {
276 } else {
277 TeXError::missing_begingroup(engine.aux, engine.state, engine.mouth)?;
278 }
279 ET::Gullet::expand_until_endgroup(engine, false, false, &tk, |_, _, t| {
280 exp.push(t);
281 Ok(())
282 })
283}
284
285pub fn fontchardp<ET: EngineTypes>(
286 engine: &mut EngineReferences<ET>,
287 tk: ET::Token,
288) -> TeXResult<ET::Dim, ET> {
289 let fnt = engine.read_font(false, &tk)?;
290 let char = engine.read_charcode(false, &tk)?;
291 Ok(fnt.get_dp(char))
292}
293pub fn fontcharht<ET: EngineTypes>(
294 engine: &mut EngineReferences<ET>,
295 tk: ET::Token,
296) -> TeXResult<ET::Dim, ET> {
297 let fnt = engine.read_font(false, &tk)?;
298 let char = engine.read_charcode(false, &tk)?;
299 Ok(fnt.get_ht(char))
300}
301pub fn fontcharwd<ET: EngineTypes>(
302 engine: &mut EngineReferences<ET>,
303 tk: ET::Token,
304) -> TeXResult<ET::Dim, ET> {
305 let fnt = engine.read_font(false, &tk)?;
306 let char = engine.read_charcode(false, &tk)?;
307 Ok(fnt.get_wd(char))
308}
309pub fn fontcharic<ET: EngineTypes>(
310 engine: &mut EngineReferences<ET>,
311 tk: ET::Token,
312) -> TeXResult<ET::Dim, ET> {
313 let fnt = engine.read_font(false, &tk)?;
314 let char = engine.read_charcode(false, &tk)?;
315 Ok(fnt.get_ic(char))
316}
317
318pub fn ifcsname<ET: EngineTypes>(
319 engine: &mut EngineReferences<ET>,
320 _tk: ET::Token,
321) -> TeXResult<bool, ET> {
322 let name = engine.read_csname()?;
323 Ok(engine.state.get_command(&name).is_some())
324}
325
326pub fn ifdefined<ET: EngineTypes>(
327 engine: &mut EngineReferences<ET>,
328 tk: ET::Token,
329) -> TeXResult<bool, ET> {
330 Ok(match engine.need_next(false, &tk)?.to_enum() {
331 StandardToken::Character(c, CommandCode::Active) => {
332 engine.state.get_ac_command(c).is_some()
333 }
334 StandardToken::ControlSequence(name) => engine.state.get_command(&name).is_some(),
335 _ => true,
336 })
337}
338
339pub fn iffontchar<ET: EngineTypes>(
340 engine: &mut EngineReferences<ET>,
341 tk: ET::Token,
342) -> TeXResult<bool, ET> {
343 let font = engine.read_font(false, &tk)?;
344 let char = engine.read_charcode(false, &tk)?;
345 Ok(font.has_char(char))
346}
347
348pub fn numexpr<ET: EngineTypes>(
349 engine: &mut EngineReferences<ET>,
350 tk: ET::Token,
351) -> TeXResult<ET::Int, ET> {
352 let (i, r) = expr_loop(
353 engine,
354 crate::engine::gullet::methods::read_int_byte,
355 crate::engine::gullet::methods::read_int_command,
356 &tk,
357 )?;
358 if let Some(r) = r {
359 if !r.is_cs_or_active() {
360 engine.requeue(r)?
361 } else {
362 match ET::Gullet::char_or_primitive(engine.state, &r) {
363 Some(CharOrPrimitive::Primitive(name)) if name == PRIMITIVES.relax => (),
364 _ => engine.requeue(r)?,
365 }
366 }
367 }
368 Ok(i)
369}
370
371pub fn dimexpr<ET: EngineTypes>(
372 engine: &mut EngineReferences<ET>,
373 tk: ET::Token,
374) -> TeXResult<ET::Dim, ET> {
375 let (i, r) = expr_loop(
376 engine,
377 crate::engine::gullet::methods::read_dim_byte,
378 crate::engine::gullet::methods::read_dim_command,
379 &tk,
380 )?;
381 if let Some(r) = r {
382 if !r.is_cs_or_active() {
383 engine.requeue(r)?
384 } else {
385 match ET::Gullet::char_or_primitive(engine.state, &r) {
386 Some(CharOrPrimitive::Primitive(name)) if name == PRIMITIVES.relax => (),
387 _ => engine.requeue(r)?,
388 }
389 }
390 }
391 Ok(i)
392}
393
394pub fn glueexpr<ET: EngineTypes>(
395 engine: &mut EngineReferences<ET>,
396 tk: ET::Token,
397) -> TeXResult<Skip<ET::Dim>, ET> {
398 let (i, r) = expr_loop(
399 engine,
400 crate::engine::gullet::methods::read_skip_byte,
401 crate::engine::gullet::methods::read_skip_command,
402 &tk,
403 )?;
404 if let Some(r) = r {
405 if !r.is_cs_or_active() {
406 engine.requeue(r)?
407 } else {
408 match ET::Gullet::char_or_primitive(engine.state, &r) {
409 Some(CharOrPrimitive::Primitive(name)) if name == PRIMITIVES.relax => (),
410 _ => engine.requeue(r)?,
411 }
412 }
413 }
414 Ok(i)
415}
416
417pub fn muexpr<ET: EngineTypes>(
418 engine: &mut EngineReferences<ET>,
419 tk: ET::Token,
420) -> TeXResult<MuSkip<ET::MuDim>, ET> {
421 fn muskip_byte<ET: EngineTypes>(
422 engine: &mut EngineReferences<ET>,
423 is_negative: bool,
424 b: u8,
425 ) -> TeXResult<MuSkip<ET::MuDim>, ET> {
426 crate::engine::gullet::methods::read_muskip_byte(engine, is_negative, b, |d, e| {
427 crate::engine::gullet::methods::read_muskip_ii(e, d)
428 })
429 }
430 fn muskip_cmd<ET: EngineTypes>(
431 engine: &mut EngineReferences<ET>,
432 is_negative: bool,
433 cmd: TeXCommand<ET>,
434 tk: ET::Token,
435 ) -> TeXResult<MuSkip<ET::MuDim>, ET> {
436 crate::engine::gullet::methods::read_muskip_command(
437 engine,
438 is_negative,
439 cmd,
440 tk,
441 |d, e| crate::engine::gullet::methods::read_muskip_ii(e, d),
442 Ok,
443 )
444 }
445 let (i, r) = expr_loop(engine, muskip_byte, muskip_cmd, &tk)?;
446 if let Some(r) = r {
447 if !r.is_cs_or_active() {
448 engine.requeue(r)?
449 } else {
450 match ET::Gullet::char_or_primitive(engine.state, &r) {
451 Some(CharOrPrimitive::Primitive(name)) if name == PRIMITIVES.relax => (),
452 _ => engine.requeue(r)?,
453 }
454 }
455 }
456 Ok(i)
457}
458
459pub fn lastnodetype<ET: EngineTypes>(
460 engine: &mut EngineReferences<ET>,
461 _tk: ET::Token,
462) -> TeXResult<ET::Int, ET> {
463 let data = engine.stomach.data_mut();
464 Ok(match data.open_lists.last() {
465 None => match data.page.last() {
466 None => (-1).into(),
467 Some(n) => (n.nodetype().to_u8() as i32).into(),
468 },
469 Some(NodeList::Vertical { children, .. }) => match children.last() {
470 None => (-1).into(),
471 Some(n) => (n.nodetype().to_u8() as i32).into(),
472 },
473 Some(NodeList::Horizontal { children, .. }) => match children.last() {
474 None => (-1).into(),
475 Some(n) => (n.nodetype().to_u8() as i32).into(),
476 },
477 Some(NodeList::Math { children, .. }) => match children.list().last() {
478 None => (-1).into(),
479 Some(n) => (n.nodetype().to_u8() as i32).into(),
480 },
481 })
482}
483
484pub fn protected<ET: EngineTypes>(
485 engine: &mut EngineReferences<ET>,
486 tk: ET::Token,
487 outer: bool,
488 long: bool,
489 _protected: bool,
490 globally: bool,
491) -> TeXResult<(), ET> {
492 crate::expand_loop!(engine,token,
493 ResolvedToken::Cmd(Some(TeXCommand::Primitive{name,..})) => match *name {
494 n if n == PRIMITIVES.outer => return super::tex::outer(engine,token,outer,long,true,globally),
495 n if n == PRIMITIVES.long => return super::tex::long(engine,token,outer,long,true,globally),
496 n if n == PRIMITIVES.protected => return self::protected(engine,token,outer,long,true,globally),
497 n if n == PRIMITIVES.global => return super::tex::global(engine,token,outer,long,true,globally),
498 n if n == PRIMITIVES.def => return super::tex::def(engine,token,outer,long,true,globally),
499 n if n == PRIMITIVES.edef => return super::tex::edef(engine,token,outer,long,true,globally),
500 n if n == PRIMITIVES.xdef => return super::tex::xdef(engine,token,outer,long,true,globally),
501 n if n == PRIMITIVES.gdef => return super::tex::gdef(engine,token,outer,long,true,globally),
502 n if n == PRIMITIVES.relax => (),
503 r => {
504 let s = r.display(engine.state.get_escape_char()).to_string();
505 engine.requeue(token)?;
506 return Err(TeXError::General(format!("You can't use a prefix with '{}'",s)))
507 }
508 }
509 _ => {
510 let s = token.display(engine.aux.memory.cs_interner(),engine.state.get_catcode_scheme(),engine.state.get_escape_char()).to_string();
511 engine.requeue(token)?;
512 return Err(TeXError::General(format!("You can't use a prefix with '{}'",s)))
513 }
514 );
515 TeXError::file_end_while_use(engine.aux, engine.state, engine.mouth, &tk)
516}
517
518pub fn readline<ET: EngineTypes>(
519 engine: &mut EngineReferences<ET>,
520 tk: ET::Token,
521 globally: bool,
522) -> TeXResult<(), ET> {
523 let idx = engine.read_file_index(&tk)?;
524 if !engine.read_keyword("to".as_bytes())? {
525 TeXError::missing_keyword(engine.aux, engine.state, engine.mouth, &["to"])?
526 }
527 let cs = engine.read_control_sequence(&tk)?;
528 let mut ret = shared_vector::Vector::new();
529 engine
530 .filesystem
531 .readline(idx, engine.state, |t| ret.push(t))?;
532 let m = Macro {
533 long: false,
534 outer: false,
535 protected: false,
536 expansion: ret.into(),
537 signature: MacroSignature {
538 arity: 0,
539 params: engine.aux.memory.empty_list(),
540 },
541 };
542 engine.set_command(&cs, Some(TeXCommand::Macro(m)), globally);
543 Ok(())
544}
545
546pub fn scantokens<ET: EngineTypes>(
547 engine: &mut EngineReferences<ET>,
548 tk: ET::Token,
549) -> TeXResult<(), ET> {
550 engine.expand_until_bgroup(false, &tk)?;
551 let mut ret: Vec<Box<[ET::Char]>> = vec![];
552 let mut curr = vec![];
553 let mut f = |c: ET::Char| {
554 if matches!(c.try_into(), Ok(b'\n')) {
555 if curr.last().copied() == Some(b'\r'.into()) {
556 curr.pop();
557 }
558 ret.push(std::mem::take(&mut curr).into());
559 } else {
560 curr.push(c)
561 }
562 };
563 let escapechar = engine.state.get_escape_char();
564 engine.read_until_endgroup(&tk, |a, state, t| {
565 match t.to_enum() {
566 StandardToken::Character(c, _) => f(c),
568 StandardToken::ControlSequence(cs) => {
569 if let Some(esc) = escapechar {
570 f(esc);
571 }
572 let res = a.memory.cs_interner().resolve(&cs);
573 for c in res.iter() {
574 f(c)
575 }
576 if res.len() == 1 {
577 let c = res.iter().next().unwrap();
578 if state.get_catcode_scheme().get(c) == &CategoryCode::Letter {
579 f(b' '.into())
580 }
581 } else {
582 f(b' '.into())
583 }
584 }
585 StandardToken::Primitive(id) => {
586 if let Some(esc) = escapechar {
587 f(esc);
588 }
589 for c in ET::Char::string_to_iter("pdfprimitive") {
590 f(c)
591 }
592 f(b' '.into());
593 let s = id.display::<ET::Char>(None).to_string();
594 let res = ET::Char::string_to_iter(&s);
595 for c in res {
596 f(c)
597 }
598 let mut res = ET::Char::string_to_iter(&s);
599 if res.len() == 1 {
600 let c = res.next().unwrap();
601 if state.get_catcode_scheme().get(c) == &CategoryCode::Letter {
602 f(b' '.into())
603 }
604 } else {
605 f(b' '.into())
606 }
607 }
608 }
609 Ok(())
610 })?;
611 if !curr.is_empty() {
612 ret.push(curr.into())
613 }
614 engine.mouth.push_string(ret.into());
615 Ok(())
616}
617
618pub fn unexpanded<ET: EngineTypes>(
619 engine: &mut EngineReferences<ET>,
620 exp: &mut Vec<ET::Token>,
621 tk: ET::Token,
622) -> TeXResult<(), ET> {
623 engine.expand_until_bgroup(false, &tk)?;
624 engine.read_until_endgroup(&tk, |_, _, t| {
625 exp.push(t);
626 Ok(())
627 })?;
628 Ok(())
629}
630
631pub fn unless<ET: EngineTypes>(
632 engine: &mut EngineReferences<ET>,
633 tk: ET::Token,
634) -> TeXResult<(), ET> {
635 let t = engine.need_next(false, &tk)?;
636 match engine.resolve(&t) {
637 ResolvedToken::Cmd(Some(TeXCommand::Primitive {
638 name,
639 cmd: PrimitiveCommand::Conditional(cnd),
640 })) => ET::Gullet::do_conditional(engine, *name, t, *cnd, true),
641 _ => {
642 let s = t
643 .display(
644 engine.aux.memory.cs_interner(),
645 engine.state.get_catcode_scheme(),
646 engine.state.get_escape_char(),
647 )
648 .to_string();
649 engine.general_error(format!("You can't use `\\unless` before {}", s))
650 }
651 }
652}
653
654pub fn middle<ET: EngineTypes>(
655 engine: &mut EngineReferences<ET>,
656 tk: ET::Token,
657) -> TeXResult<(), ET> {
658 match engine.state.get_group_type() {
659 Some(GroupType::LeftRight) => (),
660 _ => {
661 return engine.general_error(
662 "You can't use `\\middle` outside of a `\\left`-`\\right` pair".to_string(),
663 )
664 }
665 }
666 let del = match engine.read_opt_delimiter(&tk)? {
667 None => return engine.general_error("Delimiter expected after `\\middle`".to_string()),
668 Some(c) => c,
669 };
670 ET::Stomach::add_node_m(
671 engine,
672 MathNode::Atom(MathAtom {
673 sub: None,
674 sup: None,
675 nucleus: MathNucleus::Middle(del.small.char, del.small.style),
676 }),
677 );
678 Ok(())
679}
680
681pub fn marks<ET: EngineTypes>(
682 engine: &mut EngineReferences<ET>,
683 tk: ET::Token,
684) -> TeXResult<(), ET> {
685 let i = engine.read_int(false, &tk)?.into();
686 if i < 0 {
687 return engine.general_error(format!("Illegal \\marks register: {i}"));
688 }
689 super::methods::do_marks(engine, i as usize, &tk)
690}
691
692pub fn topmarks<ET: EngineTypes>(
693 engine: &mut EngineReferences<ET>,
694 exp: &mut Vec<ET::Token>,
695 tk: ET::Token,
696) -> TeXResult<(), ET> {
697 let i = engine.read_int(false, &tk)?.into();
698 if i < 0 {
699 return engine.general_error(format!("Illegal \\topmarks register: {i}"));
700 }
701 super::methods::get_marks(engine, exp, |d| &mut d.topmarks, i as usize);
702 Ok(())
703}
704pub fn firstmarks<ET: EngineTypes>(
705 engine: &mut EngineReferences<ET>,
706 exp: &mut Vec<ET::Token>,
707 tk: ET::Token,
708) -> TeXResult<(), ET> {
709 let i = engine.read_int(false, &tk)?.into();
710 if i < 0 {
711 return engine.general_error(format!("Illegal \\firstmarks register: {i}"));
712 }
713 super::methods::get_marks(engine, exp, |d| &mut d.firstmarks, i as usize);
714 Ok(())
715}
716pub fn botmarks<ET: EngineTypes>(
717 engine: &mut EngineReferences<ET>,
718 exp: &mut Vec<ET::Token>,
719 tk: ET::Token,
720) -> TeXResult<(), ET> {
721 let i = engine.read_int(false, &tk)?.into();
722 if i < 0 {
723 return engine.general_error(format!("Illegal \\botmarks register: {i}"));
724 }
725 super::methods::get_marks(engine, exp, |d| &mut d.botmarks, i as usize);
726 Ok(())
727}
728pub fn splitfirstmarks<ET: EngineTypes>(
729 engine: &mut EngineReferences<ET>,
730 exp: &mut Vec<ET::Token>,
731 tk: ET::Token,
732) -> TeXResult<(), ET> {
733 let i = engine.read_int(false, &tk)?.into();
734 if i < 0 {
735 return engine.general_error(format!("Illegal \\splitfirstmarks register: {i}"));
736 }
737 super::methods::get_marks(engine, exp, |d| &mut d.splitfirstmarks, i as usize);
738 Ok(())
739}
740pub fn splitbotmarks<ET: EngineTypes>(
741 engine: &mut EngineReferences<ET>,
742 exp: &mut Vec<ET::Token>,
743 tk: ET::Token,
744) -> TeXResult<(), ET> {
745 let i = engine.read_int(false, &tk)?.into();
746 if i < 0 {
747 return engine.general_error(format!("Illegal \\splitbotmarks register: {i}"));
748 }
749 super::methods::get_marks(engine, exp, |d| &mut d.splitbotmarks, i as usize);
750 Ok(())
751}
752
753pub fn glueshrinkorder<ET: EngineTypes>(
754 engine: &mut EngineReferences<ET>,
755 tk: ET::Token,
756) -> TeXResult<ET::Int, ET> {
757 let dim = engine.read_skip(false, &tk)?;
758 Ok(match dim.shrink {
759 None | Some(StretchShrink::Dim(_)) => ET::Int::from(0),
760 Some(StretchShrink::Fil(_)) => ET::Int::from(1),
761 Some(StretchShrink::Fill(_)) => ET::Int::from(2),
762 Some(StretchShrink::Filll(_)) => ET::Int::from(2),
763 })
764}
765pub fn gluestretchorder<ET: EngineTypes>(
766 engine: &mut EngineReferences<ET>,
767 tk: ET::Token,
768) -> TeXResult<ET::Int, ET> {
769 let dim = engine.read_skip(false, &tk)?;
770 Ok(match dim.stretch {
771 None | Some(StretchShrink::Dim(_)) => ET::Int::from(0),
772 Some(StretchShrink::Fil(_)) => ET::Int::from(1),
773 Some(StretchShrink::Fill(_)) => ET::Int::from(2),
774 Some(StretchShrink::Filll(_)) => ET::Int::from(2),
775 })
776}
777pub fn glueshrink<ET: EngineTypes>(
778 engine: &mut EngineReferences<ET>,
779 tk: ET::Token,
780) -> TeXResult<ET::Dim, ET> {
781 use crate::tex::numerics::TeXDimen;
782 let dim = engine.read_skip(false, &tk)?;
783 Ok(match dim.shrink {
784 None => ET::Dim::default(),
785 Some(StretchShrink::Dim(d)) => d,
786 Some(StretchShrink::Fil(i)) => ET::Dim::from_sp(i),
787 Some(StretchShrink::Fill(i)) => ET::Dim::from_sp(i),
788 Some(StretchShrink::Filll(i)) => ET::Dim::from_sp(i),
789 })
790}
791pub fn gluestretch<ET: EngineTypes>(
792 engine: &mut EngineReferences<ET>,
793 tk: ET::Token,
794) -> TeXResult<ET::Dim, ET> {
795 use crate::tex::numerics::TeXDimen;
796 let dim = engine.read_skip(false, &tk)?;
797 Ok(match dim.stretch {
798 None => ET::Dim::default(),
799 Some(StretchShrink::Dim(d)) => d,
800 Some(StretchShrink::Fil(i)) => ET::Dim::from_sp(i),
801 Some(StretchShrink::Fill(i)) => ET::Dim::from_sp(i),
802 Some(StretchShrink::Filll(i)) => ET::Dim::from_sp(i),
803 })
804}
805
806const PRIMITIVE_INTS: &[&str] = &[
807 "savinghyphcodes",
808 "tracingassigns",
809 "tracinggroups",
810 "tracingifs",
811 "tracingnesting",
812 "tracingscantokens",
813 "savingvdiscards",
814 "predisplaydirection",
815 "interactionmode",
816];
817
818const PRIMITIVE_TOKS: &[&str] = &["everyeof"];
819
820pub fn register_etex_primitives<E: TeXEngine>(engine: &mut E) {
821 register_primitive_int(engine, PRIMITIVE_INTS);
822 register_primitive_toks(engine, PRIMITIVE_TOKS);
823
824 register_int(engine, "numexpr", numexpr, None);
825 register_dim(engine, "dimexpr", dimexpr, None);
826 register_skip(engine, "glueexpr", glueexpr, None);
827 register_muskip(engine, "muexpr", muexpr, None);
828
829 register_int(engine, "currentgrouplevel", currentgrouplevel, None);
830 register_int(engine, "currentgrouptype", currentgrouptype, None);
831 register_int(engine, "lastnodetype", lastnodetype, None);
832 register_int(engine, "eTeXversion", eTeXversion, None);
833 register_int(engine, "glueshrinkorder", glueshrinkorder, None);
834 register_int(engine, "gluestretchorder", gluestretchorder, None);
835
836 register_dim(engine, "fontchardp", fontchardp, None);
837 register_dim(engine, "fontcharht", fontcharht, None);
838 register_dim(engine, "fontcharwd", fontcharwd, None);
839 register_dim(engine, "fontcharic", fontcharic, None);
840 register_dim(engine, "glueshrink", glueshrink, None);
841 register_dim(engine, "gluestretch", gluestretch, None);
842
843 register_assignment(engine, "protected", |e, cmd, g| {
844 protected(e, cmd, false, false, false, g)
845 });
846 register_assignment(engine, "readline", readline);
847
848 register_conditional(engine, "ifcsname", ifcsname);
849 register_conditional(engine, "ifdefined", ifdefined);
850 register_conditional(engine, "iffontchar", iffontchar);
851
852 register_expandable(engine, "detokenize", detokenize);
853 register_expandable(engine, "expanded", expanded);
854 register_expandable(engine, "unexpanded", unexpanded);
855 register_expandable(engine, "eTeXrevision", eTeXrevision);
856
857 register_unexpandable(engine, "marks", CommandScope::Any, marks);
858 register_unexpandable(engine, "middle", CommandScope::MathOnly, middle);
859
860 register_simple_expandable(engine, "unless", unless);
861 register_simple_expandable(engine, "scantokens", scantokens);
862
863 register_expandable(engine, "topmarks", topmarks);
864 register_expandable(engine, "firstmarks", firstmarks);
865 register_expandable(engine, "botmarks", botmarks);
866 register_expandable(engine, "splitfirstmarks", splitfirstmarks);
867 register_expandable(engine, "splitbotmarks", splitbotmarks);
868
869 cmtodo!(engine, beginL);
870 cmtodo!(engine, beginR);
871 cmtodo!(engine, clubpenalties);
872 cmtodo!(engine, currentifbranch);
873 cmtodo!(engine, currentiflevel);
874 cmtodo!(engine, currentiftype);
875 cmtodo!(engine, displaywidowpenalties);
876 cmtodo!(engine, endL);
877 cmtodo!(engine, endR);
878 cmtodo!(engine, gluetomu);
879 cmtodo!(engine, interlinepenalties);
880 cmtodo!(engine, lastlinefit);
881 cmtodo!(engine, mutoglue);
882 cmtodo!(engine, pagediscards);
883 cmtodo!(engine, parshapedimen);
884 cmtodo!(engine, parshapeindent);
885 cmtodo!(engine, parshapelength);
886 cmtodo!(engine, showgroups);
887 cmtodo!(engine, showifs);
888 cmtodo!(engine, showtokens);
889 cmtodo!(engine, splitdiscards);
890 cmtodo!(engine, TeXXeTstate);
891 cmtodo!(engine, widowpenalties);
892 }