Skip to main content

retex_engine/
maincontrol.rs

1//! The chief executive (*TeX: The Program* §1029–§1054) and the semantic
2//! nest (§211–§219).
3//!
4//! `main_control` reads tokens and dispatches on the current mode and
5//! command. Assignments go to [`crate::prefixed`]. List-building commands
6//! go to the typesetting modules.
7
8use std::rc::Rc;
9
10use crate::Engine;
11use crate::cmd::Cmd;
12use crate::hash::NO_CS;
13use crate::input::{Level, TokKind};
14use crate::nodes::NodeRef;
15use crate::primitives_gen as pg;
16use crate::save::GroupCode;
17use crate::token::Token;
18
19/// A mode of TeX's semantic nest.
20#[derive(Clone, Copy, Debug, PartialEq, Eq)]
21pub enum Mode {
22    /// No mode (during `\write` expansion, …).
23    None,
24    /// Vertical mode.
25    Vertical,
26    /// Internal vertical mode.
27    InternalVertical,
28    /// Horizontal mode.
29    Horizontal,
30    /// Restricted horizontal mode.
31    RestrictedHorizontal,
32    /// Display math mode.
33    DisplayMath,
34    /// Non-display math mode.
35    Math,
36}
37
38/// The kind of a mode, ignoring whether it is inner.
39#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40pub enum ModeKind {
41    /// No mode.
42    None,
43    /// (Internal) vertical.
44    Vertical,
45    /// (Restricted) horizontal.
46    Horizontal,
47    /// (Display) math.
48    Math,
49}
50
51impl Mode {
52    /// The mode kind (`abs(mode)` in TeX).
53    pub fn abs_kind(self) -> ModeKind {
54        match self {
55            Mode::None => ModeKind::None,
56            Mode::Vertical | Mode::InternalVertical => ModeKind::Vertical,
57            Mode::Horizontal | Mode::RestrictedHorizontal => ModeKind::Horizontal,
58            Mode::DisplayMath | Mode::Math => ModeKind::Math,
59        }
60    }
61
62    /// Whether the mode is inner (`mode < 0` in TeX).
63    pub fn is_inner(self) -> bool {
64        matches!(
65            self,
66            Mode::InternalVertical | Mode::RestrictedHorizontal | Mode::Math
67        )
68    }
69
70    /// TeX's `mode > 0`.
71    pub fn is_privileged(self) -> bool {
72        matches!(self, Mode::Vertical | Mode::Horizontal | Mode::DisplayMath)
73    }
74}
75
76/// The context in which a box is being built (`box_context`).
77#[derive(Clone, Copy, Debug, PartialEq, Eq)]
78pub enum BoxContext {
79    /// Shift by the given amount and append (`\moveleft`, `\raise`, or plain
80    /// appending with 0).
81    Shift(i32),
82    /// `\setbox n=`.
83    SetBox(u32),
84    /// `\global\setbox n=`.
85    GlobalSetBox(u32),
86    /// `\shipout`.
87    Shipout,
88    /// Leaders of the given glue subtype.
89    Leaders(u16),
90}
91
92/// One level of the semantic nest (`list_state_record`).
93#[derive(Clone, Debug)]
94pub struct ListState {
95    /// The mode.
96    pub mode: Mode,
97    /// The list head (a temp node).
98    pub head: NodeRef,
99    /// The last node of the list.
100    pub tail: NodeRef,
101    /// `\prevgraf`.
102    pub prev_graf: i32,
103    /// Line where this mode was entered (negative if the mode was entered
104    /// by `\halign` or `\valign`).
105    pub mode_line: i32,
106    /// `\prevdepth` (vertical modes).
107    pub prev_depth: i32,
108    /// `\spacefactor` (horizontal modes).
109    pub space_factor: i32,
110    /// Current language (horizontal modes, `clang`).
111    pub clang: i32,
112    /// Incompleat noad (math modes).
113    pub incompleat_noad: NodeRef,
114    /// Math style (math modes).
115    pub math_style: i32,
116    /// The most recent `\left` or `\middle` fence of a math left group
117    /// (e-TeX's `delim_ptr`).
118    pub delim_ptr: NodeRef,
119}
120
121/// The `\prevdepth` value that suppresses interline glue
122/// (`ignore_depth`).
123pub const IGNORE_DEPTH: i32 = -65_536_000;
124
125impl Engine {
126    /// The current list state.
127    pub fn cur_list(&self) -> &ListState {
128        self.nest.last().expect("nest is never empty")
129    }
130
131    /// The current list state, mutably.
132    pub fn cur_list_mut(&mut self) -> &mut ListState {
133        self.nest.last_mut().expect("nest is never empty")
134    }
135
136    /// Enters a new semantic level (`push_nest`).
137    pub fn push_nest(&mut self, mode: Mode) {
138        let head = self.nodes.new_temp();
139        let cl = ListState {
140            mode,
141            head,
142            tail: head,
143            prev_graf: 0,
144            mode_line: self.input.line(),
145            prev_depth: IGNORE_DEPTH,
146            space_factor: 1000,
147            clang: 0,
148            incompleat_noad: NodeRef::NULL,
149            math_style: 0,
150            delim_ptr: NodeRef::NULL,
151        };
152        self.nest.push(cl);
153        self.mode = mode;
154    }
155
156    /// Leaves a semantic level (`pop_nest`). The list is not freed.
157    pub fn pop_nest(&mut self) {
158        if let Some(cl) = self.nest.pop() {
159            self.nodes.free(cl.head);
160        }
161        self.mode = self.cur_list().mode;
162    }
163
164    /// Appends node `p` to the current list (`tail_append`).
165    pub fn tail_append(&mut self, p: NodeRef) {
166        let tail = self.cur_list().tail;
167        self.nodes.link(tail, p);
168        self.cur_list_mut().tail = p;
169    }
170
171    /// Prints a mode name (`print_mode`).
172    pub fn print_mode(&mut self, m: Mode) {
173        let s = match m {
174            Mode::Vertical => "vertical mode",
175            Mode::Horizontal => "horizontal mode",
176            Mode::DisplayMath => "display math mode",
177            Mode::None => "no mode",
178            Mode::InternalVertical => "internal vertical mode",
179            Mode::RestrictedHorizontal => "restricted horizontal mode",
180            Mode::Math => "math mode",
181        };
182        self.printer.print_str(s);
183    }
184
185    /// The chief executive: reads and executes commands until `\end` or
186    /// `\dump` (`main_control`).
187    pub fn main_control(&mut self) {
188        let every_job = self.eqtb.toks_param(pg::assign_toks::EVERYJOB).clone();
189        if !every_job.is_empty() {
190            self.begin_token_list(every_job, TokKind::EveryJobText);
191        }
192        self.main_loop();
193    }
194
195    /// Executes `list` in a nested main loop until it is used up (LuaTeX's
196    /// local control, used by `tex.runtoks`). Lua output pushed before the
197    /// call is read first.
198    pub fn run_local(&mut self, list: Option<crate::token::TokenList>) {
199        let end: crate::token::TokenList = Rc::from(vec![Token::cs(self.cs.end_local)]);
200        // The end marker goes below the material to execute.
201        let depth = self.input.stack.len();
202        self.begin_token_list(end, TokKind::Inserted);
203        if let Some(l) = list {
204            self.begin_token_list(l, TokKind::Inserted);
205        }
206        let _ = depth;
207        self.local_level += 1;
208        self.main_loop();
209        self.local_level -= 1;
210    }
211
212    /// Like [`run_local`](Self::run_local), but the material is whatever
213    /// is already on top of the input stack above the marker inserted
214    /// here. Returns the depth at which the marker was placed.
215    pub fn push_local_marker(&mut self) {
216        let end: crate::token::TokenList = Rc::from(vec![Token::cs(self.cs.end_local)]);
217        self.begin_token_list(end, TokKind::Inserted);
218    }
219
220    /// Runs the nested main loop until the local-control marker is met.
221    pub fn run_local_loop(&mut self) {
222        self.local_level += 1;
223        self.main_loop();
224        self.local_level -= 1;
225    }
226
227    fn main_loop(&mut self) {
228        loop {
229            // big_switch:
230            self.get_x_token();
231            loop {
232                // reswitch:
233                if self.eqtb.int(pg::assign_int::TRACINGCOMMANDS) > 0 {
234                    self.show_cur_cmd_chr();
235                }
236                match self.dispatch() {
237                    Next::BigSwitch => break,
238                    Next::Reswitch => continue,
239                    Next::Return => return,
240                }
241            }
242        }
243    }
244
245    /// One step of `main_control`.
246    fn dispatch(&mut self) -> Next {
247        use ModeKind as K;
248        let kind = self.mode.abs_kind();
249        let cmd = self.cur_cmd;
250        match (kind, cmd) {
251            (K::Horizontal, Cmd::Letter | Cmd::OtherChar | Cmd::CharGiven) => {
252                self.main_loop_char();
253                Next::Reswitch
254            }
255            (K::Horizontal, Cmd::CharNum) => {
256                self.scan_char_num();
257                self.cur_chr = self.cur_val as u32;
258                self.main_loop_char();
259                Next::Reswitch
260            }
261            (K::Horizontal, Cmd::Boundary) => {
262                self.append_boundary();
263                Next::BigSwitch
264            }
265            (K::Horizontal, Cmd::Spacer) => {
266                self.app_space_main();
267                Next::BigSwitch
268            }
269            (K::Horizontal | K::Math, Cmd::ExSpace) => {
270                self.app_explicit_space();
271                Next::BigSwitch
272            }
273            // Observed: in local control (`tex.runtoks`), a space in
274            // vertical mode appends an interword space, as in horizontal
275            // mode.
276            (K::Vertical, Cmd::Spacer) if self.local_level > 0 => {
277                self.append_normal_space();
278                Next::BigSwitch
279            }
280            (_, Cmd::Relax) | (K::Vertical | K::Math, Cmd::Spacer) => Next::BigSwitch,
281            (K::Math, Cmd::Boundary) => Next::BigSwitch,
282            (_, Cmd::IgnoreSpaces) => {
283                if self.cur_chr == 0 {
284                    self.get_nonblank_noncall();
285                    Next::Reswitch
286                } else {
287                    // LuaTeX's \nospaces etc. are parameters; nothing to do.
288                    Next::BigSwitch
289                }
290            }
291            (K::Vertical, Cmd::Stop) => {
292                if self.its_all_over() {
293                    Next::Return
294                } else {
295                    Next::BigSwitch
296                }
297            }
298            (K::Vertical, Cmd::Vmove)
299            | (K::Horizontal | K::Math, Cmd::Hmove)
300            | (_, Cmd::LastItem)
301            | (_, Cmd::MacParam) => {
302                self.report_illegal_case();
303                Next::BigSwitch
304            }
305            (_, c) if c > Cmd::MAX_NON_PREFIXED => {
306                self.prefixed_command();
307                Next::BigSwitch
308            }
309            (_, Cmd::BeginGroup) => {
310                self.new_save_level(GroupCode::SemiSimple);
311                self.reset_local_counts(true);
312                Next::BigSwitch
313            }
314            (_, Cmd::EndGroup) => {
315                if self.save.cur_group == GroupCode::SemiSimple {
316                    self.end_group_text_dirs();
317                    self.unsave();
318                } else {
319                    self.off_save();
320                }
321                Next::BigSwitch
322            }
323            (_, Cmd::LeftBrace) if kind != K::Math => {
324                self.new_save_level(GroupCode::Simple);
325                self.reset_local_counts(true);
326                Next::BigSwitch
327            }
328            (_, Cmd::RightBrace) => {
329                self.handle_right_brace();
330                Next::BigSwitch
331            }
332            (_, Cmd::AfterAssignment) => {
333                self.get_token();
334                self.after_token = self.cur_tok;
335                Next::BigSwitch
336            }
337            (_, Cmd::AfterGroup) => {
338                self.get_token();
339                self.save_for_after(self.cur_tok);
340                Next::BigSwitch
341            }
342            (_, Cmd::InStream) => {
343                self.open_or_close_in();
344                Next::BigSwitch
345            }
346            (_, Cmd::Message) => {
347                self.issue_message();
348                Next::BigSwitch
349            }
350            (_, Cmd::CaseShift) => {
351                self.shift_case();
352                Next::BigSwitch
353            }
354            (_, Cmd::Xray) => {
355                self.show_whatever();
356                Next::BigSwitch
357            }
358            (_, Cmd::Extension)
359                if self.cur_chr == crate::misc::ext::END_LOCAL_CONTROL && self.local_level > 0 =>
360            {
361                Next::Return
362            }
363            (_, Cmd::Extension) => {
364                self.do_extension();
365                Next::BigSwitch
366            }
367            (_, Cmd::Normal) => {
368                self.do_normal();
369                Next::BigSwitch
370            }
371            (_, Cmd::LuaFunctionCall | Cmd::LuaBytecodeCall | Cmd::LuaCall) => {
372                let n = if self.cur_cmd == Cmd::LuaFunctionCall
373                    || self.cur_cmd == Cmd::LuaBytecodeCall
374                {
375                    self.scan_int();
376                    self.cur_val
377                } else {
378                    self.cur_chr as i32
379                };
380                if self.cur_cmd == Cmd::LuaBytecodeCall {
381                    self.call_lua_bytecode(n);
382                } else {
383                    self.call_lua_function(n);
384                }
385                Next::BigSwitch
386            }
387            (_, Cmd::PartokenName) => {
388                self.partoken_name();
389                Next::BigSwitch
390            }
391            _ => {
392                self.typeset_command();
393                Next::BigSwitch
394            }
395        }
396    }
397
398    /// Reports a command that is not allowed in this mode
399    /// (`report_illegal_case`).
400    pub fn report_illegal_case(&mut self) {
401        self.you_cant();
402        self.help(&[
403            "Sorry, but I'm not programmed to handle this case;",
404            "I'll just pretend that you didn't ask for it.",
405            "If you're in the wrong mode, you might be able to",
406            "return to the right one by typing `I}' or `I$' or `I\\par'.",
407        ]);
408        self.error();
409    }
410
411    /// Prints "You can't use … in … mode" (`you_cant`).
412    pub fn you_cant(&mut self) {
413        self.print_err("You can't use `");
414        self.print_cmd_chr(self.cur_cmd, self.cur_chr);
415        self.printer.print_str("' in ");
416        self.print_mode(self.mode);
417    }
418
419    /// Whether the mode is privileged; reports an error if not
420    /// (`privileged`).
421    pub fn privileged(&mut self) -> bool {
422        if self.mode.is_privileged() {
423            true
424        } else {
425            self.report_illegal_case();
426            false
427        }
428    }
429
430    /// Inserts a missing `$` (`insert_dollar_sign`).
431    pub fn insert_dollar_sign(&mut self) {
432        self.back_input();
433        self.cur_tok = Token::char(Cmd::MathShift, '$' as u32);
434        self.print_err("Missing $ inserted");
435        self.help(&[
436            "I've inserted a begin-math/end-math symbol since I think",
437            "you left one out. Proceed, with fingers crossed.",
438        ]);
439        self.ins_error();
440    }
441
442    /// `\end` and `\dump`: finish if everything is shipped out
443    /// (`its_all_over`).
444    fn its_all_over(&mut self) -> bool {
445        if self.privileged() {
446            let cl = self.cur_list();
447            if self.page_is_empty() && cl.head == cl.tail && self.dead_cycles == 0 {
448                return true;
449            }
450            self.back_input();
451            let b = self.nodes.new_null_box(true);
452            let hsize = self.eqtb.dimen(pg::assign_dimen::HSIZE);
453            if let Some(bd) = self.nodes.box_data_mut(b) {
454                bd.width = hsize;
455            }
456            self.tail_append(b);
457            let g = self.nodes.new_glue(
458                crate::glue::Glue {
459                    stretch: crate::glue::Glue::fixed(0).stretch + retex_arith::UNITY,
460                    stretch_order: crate::glue::FILL,
461                    ..crate::glue::Glue::ZERO
462                },
463                0,
464            );
465            self.tail_append(g);
466            let p = self.nodes.new_penalty(-0o10000000000, 0);
467            self.tail_append(p);
468            self.build_page();
469        }
470        false
471    }
472
473    /// Handles a group-ending `}` (`handle_right_brace`).
474    pub fn handle_right_brace(&mut self) {
475        match self.save.cur_group {
476            GroupCode::Simple => {
477                self.end_group_text_dirs();
478                self.unsave();
479            }
480            GroupCode::BottomLevel => {
481                self.print_err("Too many }'s");
482                self.help(&[
483                    "You've closed more groups than you opened.",
484                    "Such booboos are generally harmless, so keep going.",
485                ]);
486                self.error();
487            }
488            GroupCode::SemiSimple | GroupCode::MathShift | GroupCode::MathLeft => {
489                self.extra_right_brace()
490            }
491            _ => self.handle_right_brace_typesetting(),
492        }
493    }
494
495    fn extra_right_brace(&mut self) {
496        self.print_err("Extra }, or forgotten ");
497        match self.save.cur_group {
498            GroupCode::SemiSimple => self.print_esc_str("endgroup"),
499            GroupCode::MathShift => self.printer.print_char('$' as u32),
500            _ => self.print_esc_str("right"),
501        }
502        self.help(&[
503            "I've deleted a group-closing symbol because it seems to be",
504            "spurious, as in `$x}$'. But perhaps the } is legitimate and",
505            "you forgot something else, as in `\\hbox{$x}'. In such cases",
506            "the way to recover is to insert both the forgotten and the",
507            "deleted material, e.g., by typing `I$}'.",
508        ]);
509        self.error();
510        self.input.align_state += 1;
511    }
512
513    /// Recovers from a mismatched group end (`off_save`, §1064).
514    pub fn off_save(&mut self) {
515        if self.save.cur_group == GroupCode::BottomLevel {
516            // Drop current token and complain that it was unmatched.
517            self.print_err("Extra ");
518            self.print_cmd_chr(self.cur_cmd, self.cur_chr);
519            self.help(&["Things are pretty mixed up, but I think the worst is over."]);
520            self.error();
521            return;
522        }
523        self.back_input();
524        let mut list: Vec<Token> = Vec::new();
525        self.print_err("Missing ");
526        match self.save.cur_group {
527            GroupCode::SemiSimple => {
528                list.push(Token::cs(self.cs.frozen_end_group));
529                self.print_esc_str("endgroup");
530            }
531            GroupCode::MathShift => {
532                list.push(Token::char(Cmd::MathShift, '$' as u32));
533                self.printer.print_char('$' as u32);
534            }
535            GroupCode::MathLeft => {
536                list.push(Token::cs(self.cs.frozen_right));
537                list.push(Token::other('.' as u32));
538                self.print_esc_str("right.");
539            }
540            _ => {
541                list.push(Token::RIGHT_BRACE);
542                self.printer.print_char('}' as u32);
543            }
544        }
545        self.printer.print_str(" inserted");
546        self.ins_list(Rc::from(list));
547        self.help(&[
548            "I've inserted something that you may have forgotten.",
549            "(See the <inserted text> above.)",
550            "With luck, this will get me unwedged. But if you",
551            "really didn't forget anything, try typing `2' now; then",
552            "my insertion and my current dilemma will both disappear.",
553        ]);
554        self.error();
555    }
556
557    /// `\message` and `\errmessage` (`issue_message`).
558    fn issue_message(&mut self) {
559        let c = self.cur_chr;
560        let toks = self.scan_toks_expanded();
561        let s = self.print_to_string(|e| e.show_token_list(&toks, 0, toks.len(), 10_000_000));
562        if c == 0 {
563            if self.printer.term_offset + s.len() > self.printer.max_print_line.saturating_sub(2) {
564                self.printer.print_ln();
565            } else if self.printer.term_offset > 0 || self.printer.file_offset > 0 {
566                self.printer.print_char(' ' as u32);
567            }
568            self.printer.print_bytes(&s);
569            self.printer.update_terminal();
570        } else {
571            self.print_err("");
572            self.printer.print_bytes(&s);
573            let err_help = self.eqtb.toks_param(pg::assign_toks::ERRHELP);
574            if !err_help.is_empty() {
575                self.err.use_err_help = true;
576            } else if self.long_help_seen {
577                self.help(&["(That was another \\errmessage.)"]);
578            } else {
579                if self.err.interaction < crate::error::Interaction::ErrorStop {
580                    self.long_help_seen = true;
581                }
582                self.help(&[
583                    "This error message was generated by an \\errmessage",
584                    "command, so I can't give any explicit help.",
585                    "Pretend that you're Hercule Poirot: Examine all clues,",
586                    "and deduce the truth by order and method.",
587                ]);
588            }
589            self.error();
590            self.err.use_err_help = false;
591        }
592    }
593
594    /// `\lowercase` and `\uppercase` (`shift_case`).
595    fn shift_case(&mut self) {
596        let b = self.cur_chr;
597        let toks = self.scan_general_text();
598        let upper = b == pg::def_char_code::UCCODE + UC_LC_OFFSET;
599        let mut out: Vec<Token> = Vec::with_capacity(toks.len());
600        for &t in toks.iter() {
601            if t.is_cs() {
602                if let crate::hash::CsKind::Active(c) = *self.hash.kind(t.cs_id()) {
603                    let m = self.case_map(c, upper);
604                    if m != 0 {
605                        out.push(Token::cs(self.hash.active(m)));
606                        continue;
607                    }
608                }
609                out.push(t);
610            } else {
611                let c = t.chr();
612                let m = self.case_map(c, upper);
613                if m != 0 {
614                    out.push(Token::char(t.cmd(), m));
615                } else {
616                    out.push(t);
617                }
618            }
619        }
620        self.back_list(Rc::from(out));
621    }
622
623    fn case_map(&self, c: u32, upper: bool) -> u32 {
624        (if upper {
625            self.eqtb.uc.get(c)
626        } else {
627            self.eqtb.lc.get(c)
628        }) as u32
629    }
630
631    /// Cleans up after `main_control` ends (`final_cleanup`).
632    pub fn final_cleanup(&mut self) {
633        let c = self.cur_chr;
634        if c != 1 {
635            self.eqtb.int_par[pg::assign_int::NEWLINECHAR as usize].val = -1;
636            self.printer.new_line_char = -1;
637        }
638        if self.job_name.is_none() {
639            self.open_log_file();
640        }
641        while self.input.stack.len() > 1 {
642            match self.input.stack.last() {
643                Some(Level::Tokens(_)) => self.end_token_list(),
644                _ => self.end_file_reading(),
645            }
646        }
647        while self.input.open_parens > 0 {
648            // LuaTeX prints no space before the parenthesis.
649            self.printer.print_char(')' as u32);
650            self.input.open_parens -= 1;
651        }
652        if self.save.cur_level > crate::eqtb::LEVEL_ONE {
653            self.printer.print_nl("(");
654            self.print_esc_str("end occurred ");
655            self.printer.print_str("inside a group at level ");
656            self.printer
657                .print_int(i64::from(self.save.cur_level - crate::eqtb::LEVEL_ONE));
658            self.printer.print_char(')' as u32);
659            self.show_save_groups();
660        }
661        while !self.cond.stack.is_empty() {
662            self.printer.print_nl("(");
663            self.print_esc_str("end occurred ");
664            self.printer.print_str("when ");
665            self.print_cmd_chr(Cmd::IfTest, self.cond.cur_if);
666            if self.cond.if_line != 0 {
667                self.printer.print_str(" on line ");
668                self.printer.print_int(self.cond.if_line.into());
669            }
670            self.printer.print_str(" was incomplete)");
671            self.pop_cond();
672        }
673        if self.err.history != crate::error::History::Spotless
674            && (self.err.history == crate::error::History::WarningIssued
675                || self.err.interaction < crate::error::Interaction::ErrorStop)
676            && self.printer.selector == crate::print::Selector::TermAndLog
677        {
678            self.printer.selector = crate::print::Selector::TermOnly;
679            self.printer
680                .print_nl("(see the transcript file for additional information)");
681            self.printer.selector = crate::print::Selector::TermAndLog;
682        }
683        if c == 1 {
684            if self.ini_version {
685                self.dump_requested = true;
686                return;
687            }
688            self.printer
689                .print_nl("(\\dump is performed only by INITEX)");
690        }
691    }
692
693    /// LuaTeX's `\partokenname` (pdfTeX extension): sets the name of the
694    /// paragraph-ending token.
695    fn partoken_name(&mut self) {
696        self.get_token();
697        if self.cur_cs != NO_CS {
698            self.par_token_cs = self.cur_cs;
699        }
700    }
701}
702
703/// `\lowercase` and `\uppercase` carry the `def_char_code` modifiers of
704/// `\lccode`/`\uccode` plus this offset (LuaTeX numbering).
705pub const UC_LC_OFFSET: u32 = 333_280;
706
707/// What `main_control` does next.
708pub enum Next {
709    /// Read the next token.
710    BigSwitch,
711    /// Dispatch the current token again.
712    Reswitch,
713    /// Leave `main_control`.
714    Return,
715}