1use std::rc::Rc;
5
6use crate::Engine;
7use crate::cmd::Cmd;
8use crate::hash::{CsId, NO_CS};
9use crate::input::{Level, ScannerStatus, TokKind};
10use crate::primitives_gen::assign_int as ip;
11use crate::token::{Token, TokenList};
12
13pub const EXPAND_DEPTH: u32 = 10000;
15
16pub mod cs_name_codes {
18 pub const CSNAME: u32 = 0;
20 pub const LAST_NAMED_CS: u32 = 1;
22 pub const BEGIN_CSNAME: u32 = 2;
24}
25
26#[inline]
28fn is_match_or_end(t: Token) -> bool {
29 !t.is_cs() && (t.cmd_code() == Cmd::ParEnd as u32 || t.cmd_code() == Cmd::Stop as u32)
30}
31
32#[inline]
35fn is_brace(t: Token) -> bool {
36 !t.is_cs() && (t.cmd_code() == Cmd::LeftBrace as u32 || t.cmd_code() == Cmd::RightBrace as u32)
37}
38
39#[inline]
40fn is_left_brace(t: Token) -> bool {
41 !t.is_cs() && t.cmd_code() == Cmd::LeftBrace as u32
42}
43
44impl Engine {
45 pub fn get_x_token(&mut self) {
48 self.get_next();
49 if self.cur_cmd > Cmd::MAX_COMMAND {
50 self.get_x_token_expanding();
51 } else {
52 self.cur_tok = self.make_cur_tok();
53 }
54 }
55
56 #[inline(never)]
59 fn get_x_token_expanding(&mut self) {
60 loop {
61 if self.cur_cmd >= Cmd::Call {
62 if self.cur_cmd < Cmd::EndTemplate {
63 self.macro_call();
64 } else {
65 self.cur_cs = self.cs.frozen_endv;
66 self.cur_cmd = Cmd::Endv;
67 break;
68 }
69 } else {
70 let exec = std::mem::replace(&mut self.exec_context, false);
73 self.expand();
74 self.exec_context = exec;
75 }
76 self.get_next();
77 if self.cur_cmd <= Cmd::MAX_COMMAND {
78 break;
79 }
80 }
81 self.cur_tok = self.make_cur_tok();
82 }
83
84 pub fn x_token(&mut self) {
86 while self.cur_cmd > Cmd::MAX_COMMAND {
87 if self.cur_cmd == Cmd::EndTemplate {
88 self.cur_cs = self.cs.frozen_endv;
91 self.cur_cmd = Cmd::Endv;
92 break;
93 }
94 if self.cur_cmd >= Cmd::Call {
95 self.expand();
96 } else {
97 let exec = std::mem::replace(&mut self.exec_context, false);
100 self.expand();
101 self.exec_context = exec;
102 }
103 self.get_next();
104 }
105 self.cur_tok = self.make_cur_tok();
106 }
107
108 pub fn expand(&mut self) {
110 let cv = self.cur_val;
111 let cvl = self.cur_val_level;
112 let cg = self.cur_glue;
113 let one_step = std::mem::take(&mut self.one_step);
116 self.expand_depth += 1;
117 if self.expand_depth >= EXPAND_DEPTH {
118 self.overflow("expansion depth", i64::from(EXPAND_DEPTH));
119 }
120 loop {
121 if self.cur_cmd < Cmd::Call {
122 if self.eqtb.int(ip::TRACINGCOMMANDS) > 1 {
123 self.show_cur_cmd_chr();
124 }
125 match self.cur_cmd {
126 Cmd::TopBotMark => self.insert_mark_text(),
127 Cmd::ExpandAfter => {
128 if self.cur_chr == 0 {
129 self.get_token();
131 let t = self.cur_tok;
132 self.get_token();
133 if self.cur_cmd > Cmd::MAX_COMMAND {
134 self.one_step = true;
135 self.expand();
136 } else {
137 self.back_input();
138 }
139 self.cur_tok = t;
140 self.back_input();
141 } else {
142 self.get_token();
144 if self.cur_cmd == Cmd::IfTest
145 && self.cur_chr != crate::cond::if_codes::IF_CASE
146 {
147 self.cur_chr += crate::cond::UNLESS_CODE;
148 continue; }
150 self.print_err("You can't use `");
151 self.print_esc_str("unless");
152 self.printer.print_str("' before `");
153 self.print_cmd_chr(self.cur_cmd, self.cur_chr);
154 self.printer.print_char('\'' as u32);
155 self.help(&["Continue, and I'll forget that it ever happened."]);
156 self.back_error();
157 }
158 }
159 Cmd::NoExpand => {
160 if self.cur_chr == 0 {
161 self.no_expand();
162 } else {
163 self.primitive_cmd();
164 if self.cur_cmd > Cmd::MAX_COMMAND && self.cur_cmd != Cmd::NoExpand {
165 continue;
166 }
167 if self.cur_cmd <= Cmd::MAX_COMMAND {
168 self.back_input();
169 }
170 }
171 }
172 Cmd::CsName => self.manufacture_cs_name(),
173 Cmd::Convert => self.conv_toks(),
174 Cmd::The => self.ins_the_toks(),
175 Cmd::IfTest => self.conditional(),
176 Cmd::FiOrElse => self.fi_or_else(),
177 Cmd::Input => self.input_command(),
178 Cmd::LuaExpandableCall | Cmd::LuaLocalCall => self.lua_expandable_call(),
179 Cmd::Variable | Cmd::Feedback => self.backend_expandable(),
180 _ => {
181 self.print_err("Undefined control sequence");
182 self.help(&[
183 "The control sequence at the end of the top line",
184 "of your error message was never \\def'ed. If you have",
185 "misspelled it (e.g., `\\hobx'), type `I' and the correct",
186 "spelling (e.g., `I\\hbox'). Otherwise just continue,",
187 "and I'll forget about whatever was undefined.",
188 ]);
189 self.error();
190 }
191 }
192 } else if self.cur_cmd < Cmd::EndTemplate {
193 self.one_step = one_step;
194 self.macro_call();
195 } else {
196 self.cur_tok = Token::cs(self.cs.frozen_endv);
197 self.back_input();
198 }
199 break;
200 }
201 self.expand_depth -= 1;
202 self.cur_val = cv;
203 self.cur_val_level = cvl;
204 self.cur_glue = cg;
205 }
206
207 fn no_expand(&mut self) {
209 let save = self.input.scanner_status;
210 self.input.scanner_status = ScannerStatus::Normal;
211 self.get_token();
212 self.input.scanner_status = save;
213 let t = self.cur_tok;
214 if t.is_cs() {
215 while let Some(Level::Tokens(l)) = self.input.stack.last() {
217 if l.loc < l.list.len() || l.kind == TokKind::VTemplate {
218 break;
219 }
220 self.end_token_list();
221 }
222 let list: TokenList = Rc::from(vec![Token::cs(self.cs.frozen_dont_expand), t]);
223 self.begin_token_list(list, TokKind::BackedUp);
224 } else {
225 self.back_input();
226 }
227 }
228
229 fn primitive_cmd(&mut self) {
232 let save = self.input.scanner_status;
233 self.input.scanner_status = ScannerStatus::Normal;
234 self.get_token();
235 self.input.scanner_status = save;
236 if self.cur_cs != NO_CS
237 && let Some(p) = crate::primitives::lookup(self.hash.name(self.cur_cs))
238 {
239 self.cur_cmd = p.cmd;
240 self.cur_chr = p.chr;
241 let frozen = self.frozen_primitive(self.cur_cs);
243 self.cur_cs = frozen;
244 self.cur_tok = Token::cs(frozen);
245 if self.cur_cmd > Cmd::MAX_COMMAND {
246 return;
247 }
248 return;
249 }
250 self.cur_cmd = Cmd::Relax;
251 self.cur_chr = 0;
252 self.back_input();
253 self.cur_cmd = Cmd::NoExpand;
254 }
255
256 pub(crate) fn lookup_named_cs(&mut self, name: &[u8]) -> CsId {
260 let cs = if name.is_empty() {
261 NO_CS
262 } else {
263 match self.hash.lookup(name) {
264 Some(cs) if self.eqtb.meaning(cs).cmd != Cmd::UndefinedCs => cs,
265 _ => crate::hash::UNDEFINED_CS,
266 }
267 };
268 self.last_cs_name = cs;
269 cs
270 }
271
272 fn manufacture_cs_name(&mut self) {
274 if self.cur_chr == cs_name_codes::LAST_NAMED_CS {
275 if self.last_cs_name != NO_CS {
276 self.cur_tok = Token::cs(self.last_cs_name);
277 self.back_input();
278 }
279 return;
280 }
281 let begin = self.cur_chr == cs_name_codes::BEGIN_CSNAME;
282 let e = self.cond.is_in_csname;
283 self.cond.is_in_csname = true;
284 let mut name: Vec<u8> = Vec::new();
285 loop {
286 self.get_x_token();
287 if self.cur_cs != NO_CS {
288 break;
289 }
290 crate::utf8x::push(&mut name, self.cur_chr);
291 }
292 if self.cur_cmd != Cmd::EndCsName {
293 self.complain_missing_endcsname();
294 }
295 self.cond.is_in_csname = e;
296 if begin {
297 let cs = self.lookup_named_cs(&name);
298 if cs != NO_CS && cs != crate::hash::UNDEFINED_CS {
299 self.cur_tok = Token::cs(cs);
300 self.back_input();
301 }
302 return;
303 }
304 let cs = self.hash.lookup_or_insert(&name);
305 self.last_cs_name = cs;
306 if self.eqtb.meaning(cs).cmd == Cmd::UndefinedCs {
307 self.eq_define(
308 crate::eqtb::Loc::Cs(cs),
309 crate::eqtb::Val::Meaning(crate::eqtb::Meaning::prim(Cmd::Relax, RELAX_CHR)),
310 );
311 }
312 self.cur_tok = Token::cs(cs);
313 self.back_input();
314 }
315
316 pub fn complain_missing_endcsname(&mut self) {
318 self.print_err("Missing ");
319 self.print_esc_str("endcsname");
320 self.printer.print_str(" inserted");
321 self.help(&[
322 "The control sequence marked <to be read again> should",
323 "not appear between \\csname and \\endcsname.",
324 ]);
325 self.back_error();
326 }
327
328 pub fn insert_relax(&mut self) {
330 self.cur_tok = Token::cs(self.cur_cs);
331 self.back_input();
332 self.cur_tok = Token::cs(self.cs.frozen_relax);
333 self.back_input();
334 if let Some(Level::Tokens(t)) = self.input.stack.last_mut() {
335 t.kind = TokKind::Inserted;
336 }
337 }
338
339 fn input_command(&mut self) {
341 match self.cur_chr {
342 1 => self.input.force_eof = true,
343 2 | 3 => self.pseudo_start(self.cur_chr == 3),
344 _ => {
345 if self.files.name_in_progress {
346 self.insert_relax();
347 } else {
348 self.start_input();
349 }
350 }
351 }
352 }
353
354 fn insert_mark_text(&mut self) {
356 let mut code = self.cur_chr;
357 let class = if code >= 5 {
358 code -= 5;
359 self.scan_register_num();
360 self.cur_val as u32
361 } else {
362 0
363 };
364 if let Some(list) = self.marks.get(class, code)
365 && !list.is_empty()
366 {
367 self.begin_token_list(list, TokKind::MarkText);
368 }
369 }
370
371 pub fn macro_call(&mut self) {
373 let one_step = std::mem::take(&mut self.one_step);
374 self.check_snapshot();
375 if self.check_figure() {
376 return;
377 }
378 if self.profiler.is_some() {
379 self.profile_call(self.cur_cs);
380 }
381 if let Some(mac) = &self.eqtb.meaning(self.cur_cs).mac
383 && mac.tokens[0] == Token::END_MATCH
384 && self.eqtb.int(ip::TRACINGMACROS) <= 0
385 {
386 let tokens = mac.tokens.clone();
387 let native = (mac.native.get() != 1).then(|| Rc::clone(mac));
390 while let Some(Level::Tokens(t)) = self.input.stack.last() {
391 if t.loc < t.list.len() || t.kind == TokKind::VTemplate {
392 break;
393 }
394 self.end_token_list();
395 }
396 if let Some(mac) = native
397 && let Some(i) = self.native_of(&mac, self.cur_cs)
398 && self.run_native(i, &[], one_step)
399 {
400 return;
401 }
402 let param_start = self.input.param_stack.len();
403 self.begin_macro(tokens, 1, self.cur_cs, param_start);
404 return;
405 }
406 let saved_runaway = std::mem::take(&mut self.runaway_list);
410 self.macro_call_with_args(one_step);
411 self.runaway_list = saved_runaway;
412 }
413
414 fn macro_call_with_args(&mut self, one_step: bool) {
418 let save_scanner_status = self.input.scanner_status;
419 let save_warning_index = self.input.warning_index;
420 let save_cs = self.cur_cs;
421 self.input.warning_index = self.cur_cs;
422 let Some(mac) = self.eqtb.meaning(self.cur_cs).mac.clone() else {
423 self.input.scanner_status = save_scanner_status;
424 self.input.warning_index = save_warning_index;
425 return;
426 };
427 let tokens = mac.tokens.clone();
428 if self.eqtb.int(ip::TRACINGMACROS) > 0 {
429 self.begin_diagnostic();
430 self.printer.print_ln();
431 self.print_input_level();
432 self.print_cs(self.input.warning_index);
433 self.token_show_list(&tokens, 0, tokens.len(), 10_000_000);
434 self.end_diagnostic(false);
435 }
436 let mut r = 0usize;
437 let mut pstack: Vec<TokenList> = std::mem::take(&mut self.param_scratch);
439 pstack.clear();
440 if tokens[0] != Token::END_MATCH {
441 self.input.scanner_status = ScannerStatus::Matching;
442 let mut unbalance: i32 = 0;
443 self.long_state = self.cur_cmd;
444 if self.long_state >= Cmd::OuterCall {
445 self.long_state = if self.long_state == Cmd::OuterCall {
446 Cmd::Call
447 } else {
448 Cmd::LongCall
449 };
450 }
451 let par_token = Token::cs(self.cs.par);
452 loop {
453 self.runaway_list = std::mem::take(&mut self.arg_scratch);
458 self.runaway_list.clear();
459 let s: Option<usize>;
460 let mut match_chr = 0u32;
461 let mut m = 0i32;
462 let mut rbrace_ptr = 0usize;
463 if !is_match_or_end(tokens[r]) || tokens[r] == Token::END_MATCH {
464 s = None;
465 } else {
466 match_chr = tokens[r].chr();
467 s = Some(r + 1);
468 r += 1;
469 }
470 'cont: loop {
471 self.get_token();
472 if self.cur_tok == tokens[r] {
473 r += 1;
474 if is_match_or_end(tokens[r]) {
475 if is_left_brace(self.cur_tok) {
476 self.input.align_state -= 1;
477 }
478 break 'cont; }
480 continue 'cont;
481 }
482 if s != Some(r) {
484 match s {
485 None => {
486 self.print_err("Use of ");
487 self.sprint_cs(self.input.warning_index);
488 self.printer.print_str(" doesn't match its definition");
489 self.help(&[
490 "If you say, e.g., `\\def\\a1{...}', then you must always",
491 "put `1' after `\\a', since control sequence names are",
492 "made up of letters only. The macro here has not been",
493 "followed by the required stuff, so I'm ignoring it.",
494 ]);
495 self.error();
496 self.runaway_list.clear();
497 self.input.scanner_status = save_scanner_status;
498 self.input.warning_index = save_warning_index;
499 return;
500 }
501 Some(s) => {
502 let mut t = s;
503 loop {
504 self.runaway_list.push(tokens[t]);
505 m += 1;
506 let mut u = t + 1;
507 let mut v = s;
508 let mut resumed = false;
509 loop {
510 if u == r {
511 if self.cur_tok != tokens[v] {
512 break;
513 }
514 r = v + 1;
515 resumed = true;
516 break;
517 }
518 if tokens[u] != tokens[v] {
519 break;
520 }
521 u += 1;
522 v += 1;
523 }
524 if resumed {
525 continue 'cont;
526 }
527 t += 1;
528 if t == r {
529 break;
530 }
531 }
532 r = s;
533 }
534 }
535 }
536 if self.cur_tok == par_token && self.long_state != Cmd::LongCall {
537 self.report_runaway_argument();
538 self.input.align_state -= unbalance;
539 self.input.scanner_status = save_scanner_status;
540 self.input.warning_index = save_warning_index;
541 return;
542 }
543 if is_brace(self.cur_tok) {
544 if is_left_brace(self.cur_tok) {
545 unbalance = 1;
547 loop {
548 self.runaway_list.push(self.cur_tok);
549 self.get_token();
550 if self.cur_tok == par_token && self.long_state != Cmd::LongCall {
551 self.report_runaway_argument();
552 self.input.align_state -= unbalance;
553 self.input.scanner_status = save_scanner_status;
554 self.input.warning_index = save_warning_index;
555 return;
556 }
557 if is_brace(self.cur_tok) {
558 if is_left_brace(self.cur_tok) {
559 unbalance += 1;
560 } else {
561 unbalance -= 1;
562 if unbalance == 0 {
563 break;
564 }
565 }
566 }
567 }
568 rbrace_ptr = self.runaway_list.len();
569 self.runaway_list.push(self.cur_tok);
570 } else {
571 self.back_input();
573 self.print_err("Argument of ");
574 self.sprint_cs(self.input.warning_index);
575 self.printer.print_str(" has an extra }");
576 self.help(&[
577 "I've run across a `}' that doesn't seem to match anything.",
578 "For example, `\\def\\a#1{...}' and `\\a}' would produce",
579 "this error. If you simply proceed now, the `\\par' that",
580 "I've just inserted will cause me to report a runaway",
581 "argument that might be the root of the problem. But if",
582 "your `}' was spurious, just type `2' and it will go away.",
583 ]);
584 self.input.align_state += 1;
585 self.long_state = Cmd::Call;
586 self.cur_tok = par_token;
587 self.ins_error();
588 continue 'cont;
589 }
590 } else {
591 if self.cur_tok == Token::SPACE && is_match_or_end(tokens[r]) {
592 continue 'cont;
593 }
594 self.runaway_list.push(self.cur_tok);
595 }
596 m += 1;
597 if !is_match_or_end(tokens[r]) {
598 continue 'cont;
599 }
600 break 'cont;
601 }
602 if s.is_some() {
604 let param: TokenList = if m == 1
606 && self.runaway_list.last().is_some_and(|&t| is_brace(t))
607 && rbrace_ptr > 0
608 {
609 Rc::from(&self.runaway_list[1..rbrace_ptr])
610 } else {
611 Rc::from(&self.runaway_list[..])
612 };
613 pstack.push(param);
614 let param = &pstack[pstack.len() - 1];
615 if self.eqtb.int(ip::TRACINGMACROS) > 0 {
616 self.begin_diagnostic();
617 if !self.print_input_level() {
618 self.printer.begin_line();
619 }
620 self.printer.print_char(match_chr);
621 self.printer.print_int(pstack.len() as i64);
622 self.printer.print_str("<-");
623 self.show_token_list(param, 0, param.len(), 1000);
624 self.end_diagnostic(false);
625 }
626 }
627 self.arg_scratch = std::mem::take(&mut self.runaway_list);
628 if tokens[r] == Token::END_MATCH {
629 break;
630 }
631 }
632 }
633 while let Some(Level::Tokens(t)) = self.input.stack.last() {
635 if t.loc < t.list.len() || t.kind == TokKind::VTemplate {
636 break;
637 }
638 self.end_token_list();
639 }
640 if let Some(i) = self.native_of(&mac, save_cs)
641 && self.run_native(i, &pstack, one_step)
642 {
643 pstack.clear();
644 self.param_scratch = pstack;
645 self.input.scanner_status = save_scanner_status;
646 self.input.warning_index = save_warning_index;
647 return;
648 }
649 let param_start = self.input.param_stack.len();
650 self.input.param_stack.append(&mut pstack);
651 self.param_scratch = pstack;
652 self.begin_macro(tokens, r + 1, self.input.warning_index, param_start);
653 self.input.scanner_status = save_scanner_status;
654 self.input.warning_index = save_warning_index;
655 }
656
657 fn report_runaway_argument(&mut self) {
659 if self.long_state == Cmd::Call {
660 self.runaway();
661 self.print_err("Paragraph ended before ");
662 self.sprint_cs(self.input.warning_index);
663 self.printer.print_str(" was complete");
664 self.help(&[
665 "I suspect you've forgotten a `}', causing me to apply this",
666 "control sequence to too much text. How can we recover?",
667 "My plan is to forget the whole thing and hope for the best.",
668 ]);
669 self.back_error();
670 }
671 }
672
673 pub fn runaway(&mut self) {
675 let what = match self.input.scanner_status {
676 ScannerStatus::Defining => "definition",
677 ScannerStatus::Matching => "argument",
678 ScannerStatus::Aligning => "preamble",
679 ScannerStatus::Absorbing => "text",
680 _ => return,
681 };
682 self.printer.print_nl("Runaway ");
683 self.printer.print_str(what);
684 self.printer.print_char('?' as u32);
685 self.printer.print_ln();
686 let list = std::mem::take(&mut self.runaway_list);
687 let limit = self.printer.error_line.saturating_sub(10);
688 self.show_token_list(&list, 0, list.len(), limit as i64);
689 self.runaway_list = list;
690 }
691
692 pub fn check_outer_validity(&mut self) {
695 if self.input.scanner_status == ScannerStatus::Normal {
696 return;
697 }
698 if self.eqtb.int(ip::SUPPRESSOUTERERROR) > 0 && self.cur_cs != NO_CS {
699 return;
700 }
701 self.err.deletions_allowed = false;
702 if self.cur_cs != NO_CS {
704 let reread = match self.input.stack.last() {
705 Some(Level::Tokens(_)) => true,
706 Some(Level::Text(t)) => !matches!(
707 t.source,
708 crate::input::LineSource::Terminal | crate::input::LineSource::Read(_)
709 ),
710 None => true,
711 };
712 if reread {
713 let list: TokenList = Rc::from(vec![Token::cs(self.cur_cs)]);
714 self.back_list(list);
715 }
716 self.cur_cmd = Cmd::Spacer;
717 self.cur_chr = ' ' as u32;
718 }
719 if self.input.scanner_status > ScannerStatus::Skipping {
720 self.runaway();
721 if self.cur_cs == NO_CS {
722 self.print_err("File ended");
723 } else {
724 self.cur_cs = NO_CS;
725 self.print_err("Forbidden control sequence found");
726 }
727 self.printer.print_str(" while scanning ");
728 let mut ins: Vec<Token> = Vec::new();
729 match self.input.scanner_status {
730 ScannerStatus::Defining => {
731 self.printer.print_str("definition");
732 ins.push(Token::RIGHT_BRACE);
733 }
734 ScannerStatus::Matching => {
735 self.printer.print_str("use");
736 ins.push(Token::cs(self.cs.par));
737 self.long_state = Cmd::OuterCall;
738 }
739 ScannerStatus::Aligning => {
740 self.printer.print_str("preamble");
741 ins.push(Token::cs(self.cs.frozen_cr));
742 ins.push(Token::RIGHT_BRACE);
743 self.input.align_state = -1_000_000;
744 }
745 _ => {
746 self.printer.print_str("text");
747 ins.push(Token::RIGHT_BRACE);
748 }
749 }
750 self.ins_list(Rc::from(ins));
751 self.printer.print_str(" of ");
752 self.sprint_cs(self.input.warning_index);
753 self.help(&[
754 "I suspect you have forgotten a `}', causing me",
755 "to read past where you wanted me to stop.",
756 "I'll try to recover; but if the error is serious,",
757 "you'd better type `E' or `X' now and fix your file.",
758 ]);
759 self.error();
760 } else {
761 self.print_err("Incomplete ");
762 self.print_cmd_chr(Cmd::IfTest, self.cond.cur_if);
763 self.printer.print_str("; all text was ignored after line ");
764 self.printer.print_int(self.cond.skip_line.into());
765 let third = if self.cur_cs != NO_CS {
766 self.cur_cs = NO_CS;
767 "the matching `\\fi'. I've inserted a `\\fi'; this might work."
768 } else {
769 "The file ended while I was skipping conditional text."
770 };
771 self.help(&[
772 "A forbidden control sequence occurred in skipped text.",
773 "This kind of error happens when you say `\\if...' and forget",
774 third,
775 ]);
776 self.cur_tok = Token::cs(self.cs.frozen_fi);
777 self.ins_error();
778 }
779 self.err.deletions_allowed = true;
780 }
781
782 fn frozen_primitive(&mut self, cs: CsId) -> CsId {
785 let name = self.hash.name(cs).to_vec();
786 if let Some(&f) = self.frozen_prims.get(&name) {
787 return f;
788 }
789 let f = self.hash.new_frozen(&name);
790 if let Some(p) = crate::primitives::lookup(&name) {
791 self.eqtb.ensure_cs(f);
792 self.eqtb.cs[f.0 as usize].val = crate::eqtb::Meaning::prim(p.cmd, p.chr);
793 }
794 self.frozen_prims.insert(name, f);
795 f
796 }
797}
798
799pub const RELAX_CHR: u32 = 0x11_0000;