1use crate::commands::methods::MacroParser;
6use crate::commands::primitives::{PrimitiveIdentifier, PRIMITIVES};
7use crate::engine::fontsystem::Font;
8use crate::engine::fontsystem::FontSystem;
9use crate::engine::mouth::strings::InputTokenizer;
10use crate::engine::state::State;
11use crate::engine::{EngineAux, EngineReferences, EngineTypes};
12use crate::tex::catcodes::{CategoryCodeScheme, CommandCode};
13use crate::tex::characters::StringLineSource;
14use crate::tex::nodes::boxes::{BoxInfo, TeXBox};
15use crate::tex::nodes::WhatsitFunction;
16use crate::tex::numerics::{MuSkip, Skip, TeXInt};
17use crate::tex::tokens::control_sequences::CSName;
18use crate::tex::tokens::token_lists::{CharWrite, StringCharWrite, TokenList};
19use crate::tex::tokens::Token;
20use crate::utils::errors::TeXResult;
21use either::Either;
22use std::fmt::Display;
23
24pub mod etex;
25pub mod methods;
26pub mod primitives;
27pub mod tex;
28
29#[derive(Debug)]
31pub enum ResolvedToken<'a, ET: EngineTypes> {
32 Tk { char: ET::Char, code: CommandCode },
34 Cmd(Option<&'a TeXCommand<ET>>),
37}
38
39#[derive(Debug)]
41pub enum CharOrPrimitive<ET: EngineTypes> {
42 Char(ET::Char, CommandCode),
43 Primitive(PrimitiveIdentifier),
44}
45
46#[derive(Copy, Clone, Eq, PartialEq, Debug)]
48pub enum ActiveConditional<I: TeXInt> {
49 Unfinished(PrimitiveIdentifier),
51 Case(I),
53 True(PrimitiveIdentifier),
55 Else(PrimitiveIdentifier),
57}
58impl<I: TeXInt> ActiveConditional<I> {
59 pub fn name(&self) -> PrimitiveIdentifier {
61 match self {
62 Self::Case(_) => PRIMITIVES.ifcase,
63 Self::True(n) | Self::Unfinished(n) | Self::Else(n) => *n,
64 }
65 }
66}
67
68#[derive(Clone, Debug)]
70pub enum TeXCommand<ET: EngineTypes> {
71 Macro(Macro<ET::Token>),
73 Char { char: ET::Char, code: CommandCode },
75 CharDef(ET::Char),
77 MathChar(u32),
79 Font(<ET::FontSystem as FontSystem>::Font),
81 IntRegister(usize),
83 DimRegister(usize),
85 SkipRegister(usize),
87 MuSkipRegister(usize),
89 ToksRegister(usize),
91 Primitive {
93 name: PrimitiveIdentifier,
94 cmd: PrimitiveCommand<ET>,
95 },
96}
97impl<ET: EngineTypes> TeXCommand<ET> {
98 pub const fn meaning<'a>(
100 &'a self,
101 int: &'a <<ET::Token as Token>::CS as CSName<ET::Char>>::Handler,
102 cc: &'a CategoryCodeScheme<ET::Char>,
103 escapechar: Option<ET::Char>,
104 ) -> Meaning<'a, ET> {
105 Meaning {
106 cmd: self,
107 int,
108 cc,
109 escapechar,
110 }
111 }
112
113 pub fn the<F: FnMut(&mut EngineAux<ET>, &ET::State, &mut ET::Gullet, ET::Token)>(
117 &self,
118 engine: &mut EngineReferences<ET>,
119 token: ET::Token,
120 mut cont: F,
121 ) -> TeXResult<(), ET> {
122 use crate::tex::tokens::token_lists::Otherize;
123 use std::fmt::Write;
124 match self {
125 Self::IntRegister(u) => {
126 let val = engine.state.get_int_register(*u);
127 write!(
128 Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
129 "{val}"
130 )?;
131 }
132 Self::DimRegister(u) => {
133 let val = engine.state.get_dim_register(*u);
134 write!(
135 Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
136 "{val}"
137 )?;
138 }
139 Self::SkipRegister(u) => {
140 let val = engine.state.get_skip_register(*u);
141 write!(
142 Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
143 "{val}"
144 )?;
145 }
146 Self::MuSkipRegister(u) => {
147 let val = engine.state.get_muskip_register(*u);
148 write!(
149 Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
150 "{val}"
151 )?;
152 }
153 Self::CharDef(c) => {
154 let val: u64 = (*c).into();
155 write!(
156 Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
157 "{val}"
158 )?;
159 }
160 Self::MathChar(u) => {
161 write!(
162 Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
163 "{u}"
164 )?;
165 }
166 Self::ToksRegister(u) => {
167 for t in &engine.state.get_toks_register(*u).0 {
168 cont(engine.aux, engine.state, engine.gullet, t.clone());
169 }
170 }
171 Self::Font(fnt) => {
172 let t = fnt.name();
173 cont(
174 engine.aux,
175 engine.state,
176 engine.gullet,
177 ET::Token::from_cs(t.clone()),
178 );
179 }
180 Self::Primitive { name, cmd } => return cmd.the(engine, token, *name, cont),
181 o => engine.general_error(format!(
182 "You can't use {} after \\the",
183 o.meaning(
184 engine.aux.memory.cs_interner(),
185 engine.state.get_catcode_scheme(),
186 engine.state.get_escape_char()
187 )
188 ))?,
189 }
190 Ok(())
191 }
192}
193
194#[derive(Copy, Clone, Debug)]
198pub enum PrimitiveCommand<ET: EngineTypes> {
199 Conditional(fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<bool, ET>),
201 Expandable(fn(&mut EngineReferences<ET>, &mut Vec<ET::Token>, ET::Token) -> TeXResult<(), ET>),
203 SimpleExpandable(fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<(), ET>),
206 Unexpandable {
208 scope: CommandScope,
209 apply: fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<(), ET>,
210 },
211 Assignment(fn(&mut EngineReferences<ET>, ET::Token, bool) -> TeXResult<(), ET>),
214 Int {
217 read: fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<ET::Int, ET>,
218 assign: Option<
219 for<'a, 'b> fn(&'a mut EngineReferences<'b, ET>, ET::Token, bool) -> TeXResult<(), ET>,
220 >,
221 },
222 Dim {
225 read: fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<ET::Dim, ET>,
226 assign: Option<
227 for<'a, 'b> fn(&'a mut EngineReferences<'b, ET>, ET::Token, bool) -> TeXResult<(), ET>,
228 >,
229 },
230 Skip {
233 read: fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<Skip<ET::Dim>, ET>,
234 assign: Option<
235 for<'a, 'b> fn(&'a mut EngineReferences<'b, ET>, ET::Token, bool) -> TeXResult<(), ET>,
236 >,
237 },
238 MuSkip {
241 read: fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<MuSkip<ET::MuDim>, ET>,
242 assign: Option<
243 for<'a, 'b> fn(&'a mut EngineReferences<'b, ET>, ET::Token, bool) -> TeXResult<(), ET>,
244 >,
245 },
246 FontCmd {
249 read: fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<ET::Font, ET>,
250 assign: Option<
251 for<'a, 'b> fn(&'a mut EngineReferences<'b, ET>, ET::Token, bool) -> TeXResult<(), ET>,
252 >,
253 },
254 Box(
258 fn(
259 &mut EngineReferences<ET>,
260 ET::Token,
261 ) -> TeXResult<Either<Option<TeXBox<ET>>, BoxInfo<ET>>, ET>,
262 ),
263 PrimitiveInt,
265 PrimitiveDim,
267 PrimitiveSkip,
269 PrimitiveMuSkip,
271 PrimitiveToks,
273 Whatsit {
276 get: fn(
277 &mut EngineReferences<ET>,
278 ET::Token,
279 ) -> TeXResult<Option<Box<WhatsitFunction<ET>>>, ET>,
280 immediate: fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<(), ET>,
281 the: Option<fn(&mut EngineReferences<ET>, ET::Token) -> TeXResult<Vec<ET::Token>, ET>>,
282 },
283 Relax,
285}
286impl<ET: EngineTypes> PrimitiveCommand<ET> {
287 pub fn the<F: FnMut(&mut EngineAux<ET>, &ET::State, &mut ET::Gullet, ET::Token)>(
291 &self,
292 engine: &mut EngineReferences<ET>,
293 token: ET::Token,
294 name: PrimitiveIdentifier,
295 mut cont: F,
296 ) -> TeXResult<(), ET> {
297 use crate::tex::tokens::token_lists::Otherize;
298 use std::fmt::Write;
299 match self {
300 Self::Int { read, .. } => {
301 let val = read(engine, token)?;
302 write!(
303 Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
304 "{val}"
305 )?;
306 }
307 Self::Dim { read, .. } => {
308 let val = read(engine, token)?;
309 write!(
310 Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
311 "{val}"
312 )?;
313 }
314 Self::Skip { read, .. } => {
315 let val = read(engine, token)?;
316 write!(
317 Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
318 "{val}"
319 )?;
320 }
321 Self::MuSkip { read, .. } => {
322 let val = read(engine, token)?;
323 write!(
324 Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
325 "{val}"
326 )?;
327 }
328 Self::PrimitiveInt => {
329 let val = engine.state.get_primitive_int(name);
330 write!(
331 Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
332 "{val}"
333 )?;
334 }
335 Self::PrimitiveDim => {
336 let val = engine.state.get_primitive_dim(name);
337 write!(
338 Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
339 "{val}"
340 )?;
341 }
342 Self::PrimitiveSkip => {
343 let val = engine.state.get_primitive_skip(name);
344 write!(
345 Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
346 "{val}"
347 )?;
348 }
349 Self::PrimitiveMuSkip => {
350 let val = engine.state.get_primitive_muskip(name);
351 write!(
352 Otherize::new(&mut |t| cont(engine.aux, engine.state, engine.gullet, t)),
353 "{val}"
354 )?;
355 }
356 Self::PrimitiveToks => {
357 for t in &engine.state.get_primitive_tokens(name).0 {
358 cont(engine.aux, engine.state, engine.gullet, t.clone());
359 }
360 }
361 Self::FontCmd { read, .. } => {
362 let fnt = read(engine, token)?;
363 let t = fnt.name();
364 cont(
365 engine.aux,
366 engine.state,
367 engine.gullet,
368 ET::Token::from_cs(t.clone()),
369 );
370 }
371 Self::Whatsit { the: Some(the), .. } => {
372 for t in the(engine, token)? {
373 cont(engine.aux, engine.state, engine.gullet, t);
374 }
375 }
376 _ if name == PRIMITIVES.toks => {
377 let u = engine.read_register_index(false, &token)?;
378 for t in &engine.state.get_toks_register(u).0 {
379 cont(engine.aux, engine.state, engine.gullet, t.clone());
380 }
381 }
382 _ => engine.general_error(format!(
383 "You can't use {} after \\the",
384 name.display(engine.state.get_escape_char())
385 ))?,
386 }
387 Ok(())
388 }
389}
390
391pub struct Meaning<'a, ET: EngineTypes> {
393 cmd: &'a TeXCommand<ET>,
394 int: &'a <<ET::Token as Token>::CS as CSName<ET::Char>>::Handler,
395 cc: &'a CategoryCodeScheme<ET::Char>,
396 escapechar: Option<ET::Char>,
397}
398impl<ET: EngineTypes> Meaning<'_, ET> {
399 pub fn write_chars<W: CharWrite<ET::Char, ET::CSName>>(&self, f: &mut W) -> std::fmt::Result {
403 match self.cmd {
404 TeXCommand::Macro(m) => m.meaning_char(self.int, self.cc, self.escapechar, f),
405 TeXCommand::Char { char, code } => code.meaning(*char, f),
406 TeXCommand::CharDef(c) => {
407 if let Some(e) = self.escapechar {
408 f.push_char(e);
409 }
410 write!(f, "char\"{:X}", Into::<u64>::into(*c))
411 }
412 TeXCommand::MathChar(u) => {
413 if let Some(e) = self.escapechar {
414 f.push_char(e);
415 }
416 write!(f, "mathchar\"{u:X}")
417 }
418 TeXCommand::Font(i) => {
419 write!(f, "select font ")?;
420 i.display(self.int, f)
421 }
422 TeXCommand::IntRegister(i) => {
423 if let Some(e) = self.escapechar {
424 f.push_char(e);
425 }
426 write!(f, "count{i}")
427 }
428 TeXCommand::DimRegister(i) => {
429 if let Some(e) = self.escapechar {
430 f.push_char(e);
431 }
432 write!(f, "dimen{i}")
433 }
434 TeXCommand::SkipRegister(i) => {
435 if let Some(e) = self.escapechar {
436 f.push_char(e);
437 }
438 write!(f, "skip{i}")
439 }
440 TeXCommand::MuSkipRegister(i) => {
441 if let Some(e) = self.escapechar {
442 f.push_char(e);
443 }
444 write!(f, "muskip{i}")
445 }
446 TeXCommand::ToksRegister(i) => {
447 if let Some(e) = self.escapechar {
448 f.push_char(e);
449 }
450 write!(f, "toks{i}")
451 }
452 TeXCommand::Primitive { name, .. } => {
453 write!(f, "{}", name.display(self.escapechar))
454 }
455 }
456 }
457}
458impl<ET: EngineTypes> Display for Meaning<'_, ET> {
459 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
460 self.write_chars(&mut StringCharWrite::new(f))
461 }
462}
463
464#[derive(Clone, Debug)]
466pub struct MacroSignature<T: Token> {
467 pub arity: u8,
469 pub params: TokenList<T>,
471}
472
473#[derive(Clone, Debug)]
475pub struct Macro<T: Token> {
476 pub protected: bool,
478 pub long: bool,
480 pub outer: bool,
482 pub expansion: TokenList<T>,
484 pub signature: MacroSignature<T>,
486}
487impl<T: Token> Macro<T> {
488 pub fn new<Sig: AsRef<str>, Exp: AsRef<str>, ET: EngineTypes<Token = T, Char = T::Char>>(
493 int: &mut <T::CS as CSName<T::Char>>::Handler,
494 cc: &CategoryCodeScheme<T::Char>,
495 sig: Sig,
496 exp: Exp,
497 ) -> TeXResult<Self, ET> {
498 let mut parser = MacroParser::new();
499 let sig = sig.as_ref();
500 if !sig.is_empty() {
501 let sigsrc: StringLineSource<T::Char> = sig.into();
502 let mut sigsrc = InputTokenizer::new(sigsrc);
503 while let Some(t) = sigsrc.get_next(int, cc, None)? {
504 parser.do_signature_token::<ET>(t)?;
505 }
506 }
507 let exp = exp.as_ref();
508 let expsrc: StringLineSource<T::Char> = exp.into();
509 let mut expsrc = InputTokenizer::new(expsrc);
510 while let Some(t) = expsrc.get_next(int, cc, None)? {
511 parser.do_expansion_token::<ET>(t)?;
512 }
513 Ok(parser.close(false, false, false))
514 }
515
516 pub fn meaning<'a>(
518 &'a self,
519 int: &'a <T::CS as CSName<T::Char>>::Handler,
520 cc: &'a CategoryCodeScheme<T::Char>,
521 escapechar: Option<T::Char>,
522 ) -> impl Display + 'a {
523 MacroMeaning {
524 cmd: self,
525 int,
526 cc,
527 escapechar,
528 }
529 }
530 pub fn meaning_char<F: CharWrite<T::Char, T::CS>>(
534 &self,
535 int: &<T::CS as CSName<T::Char>>::Handler,
536 cc: &CategoryCodeScheme<T::Char>,
537 escapechar: Option<T::Char>,
538 f: &mut F,
539 ) -> std::fmt::Result {
540 if self.protected {
541 if let Some(e) = escapechar {
542 f.push_char(e);
543 }
544 write!(f, "protected ")?;
545 }
546 if self.long {
547 if let Some(e) = escapechar {
548 f.push_char(e);
549 }
550 write!(f, "long ")?;
551 }
552 if self.outer {
553 if let Some(e) = escapechar {
554 f.push_char(e);
555 }
556 write!(f, "outer ")?;
557 }
558 write!(f, "macro:")?;
559 self.signature
560 .params
561 .display(int, cc, escapechar, false)
562 .fmt_cw(f)?;
563 write!(f, "->")?;
564 self.expansion.display(int, cc, escapechar, true).fmt_cw(f)
565 }
566}
567
568struct MacroMeaning<'a, T: Token> {
569 cmd: &'a Macro<T>,
570 int: &'a <T::CS as CSName<T::Char>>::Handler,
571 cc: &'a CategoryCodeScheme<T::Char>,
572 escapechar: Option<T::Char>,
573}
574impl<T: Token> Display for MacroMeaning<'_, T> {
575 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
576 self.cmd.meaning_char(
577 self.int,
578 self.cc,
579 self.escapechar,
580 &mut StringCharWrite::new(f),
581 )
582 }
583}
584
585#[derive(Clone, Debug, Copy)]
587pub enum CommandScope {
588 SwitchesToVertical,
592 SwitchesToHorizontal,
595 MathOnly,
598 SwitchesToHorizontalOrMath,
601 Any,
603}