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.profiler.is_some() {
376 self.profile_call(self.cur_cs);
377 }
378 if let Some(mac) = &self.eqtb.meaning(self.cur_cs).mac
380 && mac.tokens[0] == Token::END_MATCH
381 && self.eqtb.int(ip::TRACINGMACROS) <= 0
382 {
383 let tokens = mac.tokens.clone();
384 let native = (mac.native.get() != 1).then(|| Rc::clone(mac));
387 while let Some(Level::Tokens(t)) = self.input.stack.last() {
388 if t.loc < t.list.len() || t.kind == TokKind::VTemplate {
389 break;
390 }
391 self.end_token_list();
392 }
393 if let Some(mac) = native
394 && let Some(i) = self.native_of(&mac, self.cur_cs)
395 && self.run_native(i, &[], one_step)
396 {
397 return;
398 }
399 let param_start = self.input.param_stack.len();
400 self.begin_macro(tokens, 1, self.cur_cs, param_start);
401 return;
402 }
403 let saved_runaway = std::mem::take(&mut self.runaway_list);
407 self.macro_call_with_args(one_step);
408 self.runaway_list = saved_runaway;
409 }
410
411 fn macro_call_with_args(&mut self, one_step: bool) {
415 let save_scanner_status = self.input.scanner_status;
416 let save_warning_index = self.input.warning_index;
417 let save_cs = self.cur_cs;
418 self.input.warning_index = self.cur_cs;
419 let Some(mac) = self.eqtb.meaning(self.cur_cs).mac.clone() else {
420 self.input.scanner_status = save_scanner_status;
421 self.input.warning_index = save_warning_index;
422 return;
423 };
424 let tokens = mac.tokens.clone();
425 if self.eqtb.int(ip::TRACINGMACROS) > 0 {
426 self.begin_diagnostic();
427 self.printer.print_ln();
428 self.print_input_level();
429 self.print_cs(self.input.warning_index);
430 self.token_show_list(&tokens, 0, tokens.len(), 10_000_000);
431 self.end_diagnostic(false);
432 }
433 let mut r = 0usize;
434 let mut pstack: Vec<TokenList> = std::mem::take(&mut self.param_scratch);
436 pstack.clear();
437 if tokens[0] != Token::END_MATCH {
438 self.input.scanner_status = ScannerStatus::Matching;
439 let mut unbalance: i32 = 0;
440 self.long_state = self.cur_cmd;
441 if self.long_state >= Cmd::OuterCall {
442 self.long_state = if self.long_state == Cmd::OuterCall {
443 Cmd::Call
444 } else {
445 Cmd::LongCall
446 };
447 }
448 let par_token = Token::cs(self.cs.par);
449 loop {
450 self.runaway_list = std::mem::take(&mut self.arg_scratch);
455 self.runaway_list.clear();
456 let s: Option<usize>;
457 let mut match_chr = 0u32;
458 let mut m = 0i32;
459 let mut rbrace_ptr = 0usize;
460 if !is_match_or_end(tokens[r]) || tokens[r] == Token::END_MATCH {
461 s = None;
462 } else {
463 match_chr = tokens[r].chr();
464 s = Some(r + 1);
465 r += 1;
466 }
467 'cont: loop {
468 self.get_token();
469 if self.cur_tok == tokens[r] {
470 r += 1;
471 if is_match_or_end(tokens[r]) {
472 if is_left_brace(self.cur_tok) {
473 self.input.align_state -= 1;
474 }
475 break 'cont; }
477 continue 'cont;
478 }
479 if s != Some(r) {
481 match s {
482 None => {
483 self.print_err("Use of ");
484 self.sprint_cs(self.input.warning_index);
485 self.printer.print_str(" doesn't match its definition");
486 self.help(&[
487 "If you say, e.g., `\\def\\a1{...}', then you must always",
488 "put `1' after `\\a', since control sequence names are",
489 "made up of letters only. The macro here has not been",
490 "followed by the required stuff, so I'm ignoring it.",
491 ]);
492 self.error();
493 self.runaway_list.clear();
494 self.input.scanner_status = save_scanner_status;
495 self.input.warning_index = save_warning_index;
496 return;
497 }
498 Some(s) => {
499 let mut t = s;
500 loop {
501 self.runaway_list.push(tokens[t]);
502 m += 1;
503 let mut u = t + 1;
504 let mut v = s;
505 let mut resumed = false;
506 loop {
507 if u == r {
508 if self.cur_tok != tokens[v] {
509 break;
510 }
511 r = v + 1;
512 resumed = true;
513 break;
514 }
515 if tokens[u] != tokens[v] {
516 break;
517 }
518 u += 1;
519 v += 1;
520 }
521 if resumed {
522 continue 'cont;
523 }
524 t += 1;
525 if t == r {
526 break;
527 }
528 }
529 r = s;
530 }
531 }
532 }
533 if self.cur_tok == par_token && self.long_state != Cmd::LongCall {
534 self.report_runaway_argument();
535 self.input.align_state -= unbalance;
536 self.input.scanner_status = save_scanner_status;
537 self.input.warning_index = save_warning_index;
538 return;
539 }
540 if is_brace(self.cur_tok) {
541 if is_left_brace(self.cur_tok) {
542 unbalance = 1;
544 loop {
545 self.runaway_list.push(self.cur_tok);
546 self.get_token();
547 if self.cur_tok == par_token && self.long_state != Cmd::LongCall {
548 self.report_runaway_argument();
549 self.input.align_state -= unbalance;
550 self.input.scanner_status = save_scanner_status;
551 self.input.warning_index = save_warning_index;
552 return;
553 }
554 if is_brace(self.cur_tok) {
555 if is_left_brace(self.cur_tok) {
556 unbalance += 1;
557 } else {
558 unbalance -= 1;
559 if unbalance == 0 {
560 break;
561 }
562 }
563 }
564 }
565 rbrace_ptr = self.runaway_list.len();
566 self.runaway_list.push(self.cur_tok);
567 } else {
568 self.back_input();
570 self.print_err("Argument of ");
571 self.sprint_cs(self.input.warning_index);
572 self.printer.print_str(" has an extra }");
573 self.help(&[
574 "I've run across a `}' that doesn't seem to match anything.",
575 "For example, `\\def\\a#1{...}' and `\\a}' would produce",
576 "this error. If you simply proceed now, the `\\par' that",
577 "I've just inserted will cause me to report a runaway",
578 "argument that might be the root of the problem. But if",
579 "your `}' was spurious, just type `2' and it will go away.",
580 ]);
581 self.input.align_state += 1;
582 self.long_state = Cmd::Call;
583 self.cur_tok = par_token;
584 self.ins_error();
585 continue 'cont;
586 }
587 } else {
588 if self.cur_tok == Token::SPACE && is_match_or_end(tokens[r]) {
589 continue 'cont;
590 }
591 self.runaway_list.push(self.cur_tok);
592 }
593 m += 1;
594 if !is_match_or_end(tokens[r]) {
595 continue 'cont;
596 }
597 break 'cont;
598 }
599 if s.is_some() {
601 let param: TokenList = if m == 1
603 && self.runaway_list.last().is_some_and(|&t| is_brace(t))
604 && rbrace_ptr > 0
605 {
606 Rc::from(&self.runaway_list[1..rbrace_ptr])
607 } else {
608 Rc::from(&self.runaway_list[..])
609 };
610 pstack.push(param);
611 let param = &pstack[pstack.len() - 1];
612 if self.eqtb.int(ip::TRACINGMACROS) > 0 {
613 self.begin_diagnostic();
614 if !self.print_input_level() {
615 self.printer.begin_line();
616 }
617 self.printer.print_char(match_chr);
618 self.printer.print_int(pstack.len() as i64);
619 self.printer.print_str("<-");
620 self.show_token_list(param, 0, param.len(), 1000);
621 self.end_diagnostic(false);
622 }
623 }
624 self.arg_scratch = std::mem::take(&mut self.runaway_list);
625 if tokens[r] == Token::END_MATCH {
626 break;
627 }
628 }
629 }
630 while let Some(Level::Tokens(t)) = self.input.stack.last() {
632 if t.loc < t.list.len() || t.kind == TokKind::VTemplate {
633 break;
634 }
635 self.end_token_list();
636 }
637 if let Some(i) = self.native_of(&mac, save_cs)
638 && self.run_native(i, &pstack, one_step)
639 {
640 pstack.clear();
641 self.param_scratch = pstack;
642 self.input.scanner_status = save_scanner_status;
643 self.input.warning_index = save_warning_index;
644 return;
645 }
646 let param_start = self.input.param_stack.len();
647 self.input.param_stack.append(&mut pstack);
648 self.param_scratch = pstack;
649 self.begin_macro(tokens, r + 1, self.input.warning_index, param_start);
650 self.input.scanner_status = save_scanner_status;
651 self.input.warning_index = save_warning_index;
652 }
653
654 fn report_runaway_argument(&mut self) {
656 if self.long_state == Cmd::Call {
657 self.runaway();
658 self.print_err("Paragraph ended before ");
659 self.sprint_cs(self.input.warning_index);
660 self.printer.print_str(" was complete");
661 self.help(&[
662 "I suspect you've forgotten a `}', causing me to apply this",
663 "control sequence to too much text. How can we recover?",
664 "My plan is to forget the whole thing and hope for the best.",
665 ]);
666 self.back_error();
667 }
668 }
669
670 pub fn runaway(&mut self) {
672 let what = match self.input.scanner_status {
673 ScannerStatus::Defining => "definition",
674 ScannerStatus::Matching => "argument",
675 ScannerStatus::Aligning => "preamble",
676 ScannerStatus::Absorbing => "text",
677 _ => return,
678 };
679 self.printer.print_nl("Runaway ");
680 self.printer.print_str(what);
681 self.printer.print_char('?' as u32);
682 self.printer.print_ln();
683 let list = std::mem::take(&mut self.runaway_list);
684 let limit = self.printer.error_line.saturating_sub(10);
685 self.show_token_list(&list, 0, list.len(), limit as i64);
686 self.runaway_list = list;
687 }
688
689 pub fn check_outer_validity(&mut self) {
692 if self.input.scanner_status == ScannerStatus::Normal {
693 return;
694 }
695 if self.eqtb.int(ip::SUPPRESSOUTERERROR) > 0 && self.cur_cs != NO_CS {
696 return;
697 }
698 self.err.deletions_allowed = false;
699 if self.cur_cs != NO_CS {
701 let reread = match self.input.stack.last() {
702 Some(Level::Tokens(_)) => true,
703 Some(Level::Text(t)) => !matches!(
704 t.source,
705 crate::input::LineSource::Terminal | crate::input::LineSource::Read(_)
706 ),
707 None => true,
708 };
709 if reread {
710 let list: TokenList = Rc::from(vec![Token::cs(self.cur_cs)]);
711 self.back_list(list);
712 }
713 self.cur_cmd = Cmd::Spacer;
714 self.cur_chr = ' ' as u32;
715 }
716 if self.input.scanner_status > ScannerStatus::Skipping {
717 self.runaway();
718 if self.cur_cs == NO_CS {
719 self.print_err("File ended");
720 } else {
721 self.cur_cs = NO_CS;
722 self.print_err("Forbidden control sequence found");
723 }
724 self.printer.print_str(" while scanning ");
725 let mut ins: Vec<Token> = Vec::new();
726 match self.input.scanner_status {
727 ScannerStatus::Defining => {
728 self.printer.print_str("definition");
729 ins.push(Token::RIGHT_BRACE);
730 }
731 ScannerStatus::Matching => {
732 self.printer.print_str("use");
733 ins.push(Token::cs(self.cs.par));
734 self.long_state = Cmd::OuterCall;
735 }
736 ScannerStatus::Aligning => {
737 self.printer.print_str("preamble");
738 ins.push(Token::cs(self.cs.frozen_cr));
739 ins.push(Token::RIGHT_BRACE);
740 self.input.align_state = -1_000_000;
741 }
742 _ => {
743 self.printer.print_str("text");
744 ins.push(Token::RIGHT_BRACE);
745 }
746 }
747 self.ins_list(Rc::from(ins));
748 self.printer.print_str(" of ");
749 self.sprint_cs(self.input.warning_index);
750 self.help(&[
751 "I suspect you have forgotten a `}', causing me",
752 "to read past where you wanted me to stop.",
753 "I'll try to recover; but if the error is serious,",
754 "you'd better type `E' or `X' now and fix your file.",
755 ]);
756 self.error();
757 } else {
758 self.print_err("Incomplete ");
759 self.print_cmd_chr(Cmd::IfTest, self.cond.cur_if);
760 self.printer.print_str("; all text was ignored after line ");
761 self.printer.print_int(self.cond.skip_line.into());
762 let third = if self.cur_cs != NO_CS {
763 self.cur_cs = NO_CS;
764 "the matching `\\fi'. I've inserted a `\\fi'; this might work."
765 } else {
766 "The file ended while I was skipping conditional text."
767 };
768 self.help(&[
769 "A forbidden control sequence occurred in skipped text.",
770 "This kind of error happens when you say `\\if...' and forget",
771 third,
772 ]);
773 self.cur_tok = Token::cs(self.cs.frozen_fi);
774 self.ins_error();
775 }
776 self.err.deletions_allowed = true;
777 }
778
779 fn frozen_primitive(&mut self, cs: CsId) -> CsId {
782 let name = self.hash.name(cs).to_vec();
783 if let Some(&f) = self.frozen_prims.get(&name) {
784 return f;
785 }
786 let f = self.hash.new_frozen(&name);
787 if let Some(p) = crate::primitives::lookup(&name) {
788 self.eqtb.ensure_cs(f);
789 self.eqtb.cs[f.0 as usize].val = crate::eqtb::Meaning::prim(p.cmd, p.chr);
790 }
791 self.frozen_prims.insert(name, f);
792 f
793 }
794}
795
796pub const RELAX_CHR: u32 = 0x11_0000;