1use crate::commands::primitives::{PrimitiveIdentifier, PRIMITIVES};
6use crate::engine::filesystem::SourceReference;
7use crate::engine::filesystem::{File, FileLineSource};
8use crate::engine::mouth::strings::{InputTokenizer, MouthState};
9use crate::engine::state::State;
10use crate::engine::utils::outputs::Outputs;
11use crate::engine::{EngineAux, EngineReferences, EngineTypes};
12use crate::prelude::{CommandCode, TokenList};
13use crate::tex::catcodes::CategoryCodeScheme;
14use crate::tex::characters::StringLineSource;
15use crate::tex::tokens::control_sequences::CSName;
16use crate::tex::tokens::token_lists::MacroExpansion;
17use crate::tex::tokens::Token;
18use crate::utils::errors::{InvalidCharacter, RecoverableError, TeXError, TeXResult};
19
20pub mod strings;
21
22pub trait Mouth<ET: EngineTypes> {
38 fn new(aux: &mut EngineAux<ET>, state: &mut ET::State) -> Self;
40 fn push_file(&mut self, f: ET::File);
42 fn push_string(&mut self, s: StringLineSource<ET::Char>);
44 fn push_exp(&mut self, exp: &TokenList<ET::Token>);
46 fn push_vec(&mut self, exp: Vec<ET::Token>);
48 fn push_slice_rev(&mut self, exp: &[ET::Token]);
51 fn push_macro_exp(&mut self, exp: MacroExpansion<ET::Token>);
54 fn requeue(&mut self, t: ET::Token);
59 fn get_next(
64 &mut self,
65 aux: &mut EngineAux<ET>,
66 state: &ET::State,
67 ) -> Result<Option<ET::Token>, InvalidCharacter<ET::Char>>;
68 fn iterate<R, E, F: FnMut(&mut EngineAux<ET>, ET::Token) -> TeXResult<Option<R>, ET>>(
76 &mut self,
77 aux: &mut EngineAux<ET>,
78 state: &ET::State,
79 cont: F,
80 eof: E,
81 ) -> TeXResult<R, ET>
82 where
83 E: Fn(&EngineAux<ET>, &ET::State, &mut Self) -> TeXResult<(), ET>;
84 fn endinput(
88 &mut self,
89 aux: &mut EngineAux<ET>,
90 state: &ET::State,
91 ) -> Result<(), InvalidCharacter<ET::Char>>;
92 fn finish(&mut self);
94
95 fn current_sourceref(&self) -> SourceReference<<ET::File as File>::SourceRefID>;
97 fn start_ref(&self) -> SourceReference<<ET::File as File>::SourceRefID>;
104 fn update_start_ref(&mut self);
106 fn line_number(&self) -> usize;
108 fn get_args(&mut self) -> [Vec<ET::Token>; 9];
112 fn return_args(&mut self, args: [Vec<ET::Token>; 9]);
115
116 fn read_until_endgroup<E, F: FnMut(&mut EngineAux<ET>, ET::Token) -> TeXResult<(), ET>>(
122 &mut self,
123 aux: &mut EngineAux<ET>,
124 state: &ET::State,
125 mut cont: F,
126 eof: E,
127 ) -> TeXResult<ET::Token, ET>
128 where
129 E: Fn(&EngineAux<ET>, &ET::State, &mut Self) -> TeXResult<(), ET>,
130 {
131 let mut ingroups = 0;
132 self.iterate(
133 aux,
134 state,
135 |a, t| {
136 match t.command_code() {
137 CommandCode::BeginGroup => ingroups += 1,
138 CommandCode::EndGroup => {
139 if ingroups == 0 {
140 return Ok(Some(t));
141 }
142 ingroups -= 1;
143 }
144 CommandCode::Primitive if t.is_primitive() == Some(PRIMITIVES.noexpand) => {
145 return Ok(None)
146 }
147 _ => (),
148 }
149 cont(a, t)?;
150 Ok(None)
151 },
152 eof,
153 )
154 }
155
156 fn preview(
158 &self,
159 int: &<ET::CSName as CSName<ET::Char>>::Handler,
160 cc: &CategoryCodeScheme<ET::Char>,
161 esc: Option<ET::Char>,
162 ) -> String;
163
164 fn file_trace(
165 &self,
166 ) -> impl Iterator<Item = SourceReference<<ET::File as File>::SourceRefID>> + '_;
167}
168
169enum TokenSource<T: Token, F: File<Char = T::Char>> {
170 String(InputTokenizer<T::Char, StringLineSource<T::Char>>),
171 File(InputTokenizer<T::Char, F::LineSource>, F::SourceRefID),
172 Vec(Vec<T>),
173}
174
175pub struct DefaultMouth<ET: EngineTypes> {
182 inputs: Vec<TokenSource<ET::Token, ET::File>>,
183 args: Option<[Vec<ET::Token>; 9]>,
184 start_ref: Vec<SourceReference<<ET::File as File>::SourceRefID>>,
185 vecs: Vec<Vec<ET::Token>>,
186}
187
188impl<ET: EngineTypes> Mouth<ET> for DefaultMouth<ET> {
189 fn new(_aux: &mut EngineAux<ET>, _state: &mut ET::State) -> Self {
190 Self {
191 inputs: Vec::new(),
192 args: Some(array_init::array_init(|_| Vec::new())),
193 start_ref: vec![],
194 vecs: vec![],
195 }
196 }
197
198 fn finish(&mut self) {
199 for s in self.inputs.drain(..) {
200 if let TokenSource::Vec(mut v) = s {
201 v.clear();
202 self.vecs.push(v);
203 }
204 }
205 self.start_ref.clear();
206 }
207
208 fn file_trace(
209 &self,
210 ) -> impl Iterator<Item = SourceReference<<ET::File as File>::SourceRefID>> + '_ {
211 self.inputs.iter().rev().filter_map(|s| {
212 if let TokenSource::File(f, id) = s {
213 Some(SourceReference {
214 file: *id,
215 line: f.line(),
216 column: f.column(),
217 })
218 } else {
219 None
220 }
221 })
222 }
223
224 fn current_sourceref(&self) -> SourceReference<<ET::File as File>::SourceRefID> {
225 for s in self.inputs.iter().rev() {
226 if let TokenSource::File(f, id) = s {
227 return SourceReference {
228 file: *id,
229 line: f.line(),
230 column: f.column(),
231 };
232 }
233 }
234 self.start_ref.last().copied().unwrap_or_default()
235 }
236 fn start_ref(&self) -> SourceReference<<ET::File as File>::SourceRefID> {
237 self.start_ref.last().copied().unwrap_or_default()
238 }
239 fn update_start_ref(&mut self) {
240 match self.inputs.last() {
241 Some(TokenSource::File(f, id)) => {
242 let rf = SourceReference {
243 file: *id,
244 line: f.line(),
245 column: f.column(),
246 };
247 match self.start_ref.last_mut() {
248 None => self.start_ref.push(rf),
249 Some(s) => *s = rf,
250 }
251 }
252 Some(TokenSource::Vec(v)) if v.is_empty() => {
253 if let Some(TokenSource::Vec(v)) = self.inputs.pop() {
254 self.vecs.push(v);
255 } else {
256 unreachable!()
257 }
258 self.update_start_ref();
259 }
260 _ => (),
261 }
262 }
263
264 fn get_args(&mut self) -> [Vec<ET::Token>; 9] {
265 match std::mem::take(&mut self.args) {
266 Some(a) => a,
267 None => unreachable!(), }
269 }
271
272 fn return_args(&mut self, mut exp: [Vec<ET::Token>; 9]) {
273 for a in &mut exp {
274 a.clear();
275 }
276 self.args = Some(exp);
277 }
278
279 fn endinput(
280 &mut self,
281 aux: &mut EngineAux<ET>,
282 state: &ET::State,
283 ) -> Result<(), InvalidCharacter<ET::Char>> {
284 for (i, s) in self.inputs.iter().enumerate().rev() {
285 if let TokenSource::File(f, _) = s {
286 aux.outputs.file_close(f.source.path().display());
287 let TokenSource::File(mut r, _) = self.inputs.remove(i) else {
288 unreachable!()
289 };
290 self.start_ref.pop();
291 if r.state != MouthState::NewLine {
292 let mut ret = Vec::new();
293 let par = state.get_par_token();
294 match r.read(
295 aux.memory.cs_interner_mut(),
296 state.get_catcode_scheme(),
297 state.get_endline_char(),
298 &par,
299 |t| ret.push(t),
300 ) {
301 Ok(_) => (),
302 Err(e) => e.recover(aux, state, self)?,
303 }
304 self.with_list(|ls| ls.extend(ret.into_iter().rev()));
305 }
306 return Ok(());
307 }
308 }
309 Ok(())
310 }
311
312 fn line_number(&self) -> usize {
313 for s in self.inputs.iter().rev() {
314 if let TokenSource::File(s, _) = s {
315 return s.line();
316 }
317 }
318 0
319 }
320
321 fn push_macro_exp(&mut self, mut exp: MacroExpansion<ET::Token>) {
322 self.with_list(|v| exp.consume_rev(v));
323 self.return_args(exp.args);
324 }
325
326 fn push_exp(&mut self, exp: &TokenList<ET::Token>) {
327 self.with_list(|v| v.extend(exp.0.iter().rev().cloned()));
328 }
329
330 fn push_vec(&mut self, exp: Vec<ET::Token>) {
331 self.with_list(|v| v.extend(exp.into_iter().rev()));
332 }
333 fn push_slice_rev(&mut self, exp: &[ET::Token]) {
334 self.with_list(|v| v.extend(exp.iter().cloned()));
335 }
336
337 fn push_string(&mut self, s: StringLineSource<ET::Char>) {
338 self.inputs
339 .push(TokenSource::String(InputTokenizer::new(s)));
340 }
341
342 fn requeue(&mut self, t: ET::Token) {
343 self.with_list(|v| v.push(t));
344 }
345
346 fn push_file(&mut self, f: ET::File) {
347 let id = f.sourceref();
349 let s = match f.line_source() {
350 Ok(s) => s,
351 Err(p) => panic!("File {} has no line source", p.display()),
352 };
353 let rf = SourceReference {
354 file: id,
355 line: 0,
356 column: 0,
357 };
358 self.inputs
359 .push(TokenSource::File(InputTokenizer::new(s), id));
360 self.start_ref.push(rf);
361 }
362
363 fn get_next(
364 &mut self,
365 aux: &mut EngineAux<ET>,
366 state: &ET::State,
367 ) -> Result<Option<ET::Token>, InvalidCharacter<ET::Char>> {
368 while let Some(src) = self.inputs.last_mut() {
369 match src {
370 TokenSource::Vec(v) => match v.pop() {
371 Some(t) => return Ok(Some(t)),
372 _ => {
373 if let Some(TokenSource::Vec(v)) = self.inputs.pop() {
374 self.vecs.push(v);
375 }
376 }
377 },
378 TokenSource::String(s) => {
379 let par = state.get_par_token();
380 match s.get_next(
381 aux.memory.cs_interner_mut(),
382 state.get_catcode_scheme(),
383 state.get_endline_char(),
384 &par,
385 ) {
386 Ok(Some(t)) => return Ok(Some(t)),
387 Ok(None) => return Ok(Some(self.end_file(aux, state))),
388 Err(c) => {
389 c.recover::<_, InvalidCharacter<_>>(aux, state, self)?;
390 continue;
391 }
392 }
393 }
394 TokenSource::File(s, _) => {
395 let cc: &CategoryCodeScheme<ET::Char> = state.get_catcode_scheme();
396 let endline: Option<ET::Char> = state.get_endline_char();
397 let par = state.get_par_token();
398 match s.get_next(aux.memory.cs_interner_mut(), cc, endline, &par) {
399 Ok(Some(t)) => return Ok(Some(t)),
400 Ok(None) => return Ok(Some(self.end_file(aux, state))),
401 Err(c) => {
402 c.recover::<_, InvalidCharacter<_>>(aux, state, self)?;
403 continue;
404 }
405 }
406 }
407 }
408 }
409 Ok(None)
410 }
411
412 fn iterate<R, E, F: FnMut(&mut EngineAux<ET>, ET::Token) -> TeXResult<Option<R>, ET>>(
413 &mut self,
414 aux: &mut EngineAux<ET>,
415 state: &ET::State,
416 mut cont: F,
417 eof: E,
418 ) -> TeXResult<R, ET>
419 where
420 E: Fn(&EngineAux<ET>, &ET::State, &mut Self) -> TeXResult<(), ET>,
421 {
422 'top: loop {
423 match self.inputs.last_mut() {
424 Some(TokenSource::Vec(v)) => {
425 while let Some(t) = v.pop() {
426 if let Some(r) = cont(aux, t)? {
427 return Ok(r);
428 }
429 }
430 if let Some(TokenSource::Vec(v)) = self.inputs.pop() {
431 self.vecs.push(v);
432 } else {
433 unreachable!()
434 }
435 }
436 Some(TokenSource::String(s)) => {
437 let cc = state.get_catcode_scheme();
438 let endline = state.get_endline_char();
439 let par = state.get_par_token();
440 loop {
441 match s.get_next(aux.memory.cs_interner_mut(), cc, endline, &par) {
442 Ok(Some(t)) => {
443 if let Some(r) = cont(aux, t)? {
444 return Ok(r);
445 }
446 }
447 Err(c) => {
448 c.recover::<_, TeXError<_>>(aux, state, self)?;
449 continue 'top;
450 }
451 Ok(None) => {
452 eof(aux, state, self)?;
453 continue 'top;
454 }
455 }
456 }
457 }
458 Some(TokenSource::File(s, _)) => {
459 let cc = state.get_catcode_scheme();
460 let endline = state.get_endline_char();
461 let par = state.get_par_token();
462 loop {
463 match s.get_next(aux.memory.cs_interner_mut(), cc, endline, &par) {
464 Ok(Some(t)) => {
465 if let Some(r) = cont(aux, t)? {
466 return Ok(r);
467 }
468 }
469 Err(c) => {
470 c.recover::<_, TeXError<_>>(aux, state, self)?;
471 continue 'top;
472 }
473 Ok(None) => {
474 eof(aux, state, self)?;
475 continue 'top;
476 }
477 }
478 }
479 }
480 None => eof(aux, state, self)?,
481 }
482 }
483 }
484
485 fn preview(
486 &self,
487 int: &<ET::CSName as CSName<ET::Char>>::Handler,
488 cc: &CategoryCodeScheme<ET::Char>,
489 esc: Option<ET::Char>,
490 ) -> String {
491 let mut str = String::new();
492 for src in self.inputs.iter().rev() {
493 match src {
494 TokenSource::String(s) => s.preview(&mut 1000, &mut str).unwrap(),
496 TokenSource::File(s, _) => {
498 s.preview(&mut 1000, &mut str).unwrap();
499 break;
500 }
501 TokenSource::Vec(v) => {
502 for t in v.iter().rev().take(1000) {
503 t.display_fmt(int, cc, esc, &mut str).unwrap()
504 }
505 } }
509 }
510 str
511 }
512}
513
514impl<ET: EngineTypes> DefaultMouth<ET> {
515 pub fn into<ET2: EngineTypes<Token = ET::Token, File = ET::File>>(self) -> DefaultMouth<ET2> {
517 DefaultMouth {
518 inputs: self.inputs,
519 args: self.args,
520 start_ref: self.start_ref,
521 vecs: self.vecs,
522 }
523 }
524 pub fn into_tokens<
526 ET2: EngineTypes<Char = ET::Char, File = ET::File>,
527 F: FnMut(ET::Token) -> ET2::Token,
528 >(
529 self,
530 mut token: F,
531 ) -> DefaultMouth<ET2> {
532 DefaultMouth {
533 inputs: self
534 .inputs
535 .into_iter()
536 .map(|s| match s {
537 TokenSource::String(s) => TokenSource::String(s),
538 TokenSource::File(s, id) => TokenSource::File(s, id),
539 TokenSource::Vec(v) => {
540 TokenSource::Vec(v.into_iter().map(&mut token).collect())
541 }
542 })
543 .collect(),
544 args: self.args.map(|[a0, a1, a2, a3, a4, a5, a6, a7, a8]| {
545 [
546 a0.into_iter().map(&mut token).collect(),
547 a1.into_iter().map(&mut token).collect(),
548 a2.into_iter().map(&mut token).collect(),
549 a3.into_iter().map(&mut token).collect(),
550 a4.into_iter().map(&mut token).collect(),
551 a5.into_iter().map(&mut token).collect(),
552 a6.into_iter().map(&mut token).collect(),
553 a7.into_iter().map(&mut token).collect(),
554 a8.into_iter().map(&mut token).collect(),
555 ]
556 }),
557 start_ref: self.start_ref,
558 vecs: self
559 .vecs
560 .into_iter()
561 .map(|v| v.into_iter().map(&mut token).collect())
562 .collect(),
563 }
564 }
565 fn with_list<Fn: FnOnce(&mut Vec<ET::Token>)>(&mut self, f: Fn) {
566 match self.inputs.last_mut() {
567 Some(TokenSource::Vec(v)) => f(v),
568 _ => {
569 let v = match self.vecs.pop() {
570 Some(mut v) => {
571 f(&mut v);
572 v
573 }
574 None => {
575 let mut v = Vec::new();
576 f(&mut v);
577 v
578 }
579 };
580 self.inputs.push(TokenSource::Vec(v));
581 }
582 };
583 }
584 fn end_file(&mut self, aux: &mut EngineAux<ET>, state: &ET::State) -> ET::Token {
585 match self.inputs.pop() {
586 Some(TokenSource::File(f, _)) => {
587 self.start_ref.pop();
588 aux.outputs.file_close(f.source.path().display());
589 }
590 Some(TokenSource::String(_)) => (), _ => unreachable!(),
592 };
593 let everyeof = state.get_primitive_tokens(PRIMITIVES.everyeof);
594 if everyeof.is_empty() {
595 ET::Token::eof()
596 } else {
597 self.requeue(ET::Token::eof());
598 self.push_exp(everyeof);
599 if let Some(TokenSource::Vec(v)) = self.inputs.last_mut() {
600 v.pop().unwrap()
601 } else {
602 unreachable!()
603 }
604 }
605 }
606}
607
608impl<ET: EngineTypes> EngineReferences<'_, ET> {
609 pub fn push_file(&mut self, f: ET::File) {
611 self.mouth.push_file(f);
612 }
613 pub fn push_every(&mut self, every: PrimitiveIdentifier) {
615 let tks = self.state.get_primitive_tokens(every);
616 self.mouth.push_exp(tks);
617 }
618 pub fn preview(&self) -> String {
620 self.mouth.preview(
621 self.aux.memory.cs_interner(),
622 self.state.get_catcode_scheme(),
623 self.state.get_escape_char(),
624 )
625 }
626}