1use crate::Engine;
5use crate::eqtb::{CodeKind, Eqtb, LEVEL_ONE, LEVEL_ZERO, Loc, Meaning, Val};
6use crate::primitives_gen::assign_int as ip;
7use crate::token::Token;
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11#[repr(u8)]
12pub enum GroupCode {
13 BottomLevel = 0,
15 Simple = 1,
17 Hbox = 2,
19 AdjustedHbox = 3,
21 Vbox = 4,
23 Vtop = 5,
25 Align = 6,
27 NoAlign = 7,
29 Output = 8,
31 Math = 9,
33 Disc = 10,
35 Insert = 11,
37 Vcenter = 12,
39 MathChoice = 13,
41 SemiSimple = 14,
43 MathShift = 15,
45 MathLeft = 16,
47 LocalBox = 17,
49 SplitOff = 18,
51 SplitKeep = 19,
53 Preamble = 20,
55 AlignSet = 21,
57 FinRow = 22,
59}
60
61#[derive(Clone, Debug)]
63pub enum SaveEntry {
64 Restore {
66 loc: Loc,
68 old: Val,
70 level: u16,
72 },
73 InsertToken(Token),
75 Boundary {
77 group: GroupCode,
79 boundary: usize,
81 line: i32,
83 },
84 Value(i64),
86 RestoreCodes {
90 kind: CodeKind,
92 old: Box<crate::eqtb::CodeTable>,
94 upto: u32,
96 },
97}
98
99#[derive(Debug, Clone)]
101pub struct SaveStack {
102 pub stack: Vec<SaveEntry>,
104 pub cur_level: u16,
106 pub cur_group: GroupCode,
108 pub cur_boundary: usize,
110}
111
112impl Default for SaveStack {
113 fn default() -> Self {
114 SaveStack {
115 stack: Vec::new(),
116 cur_level: LEVEL_ONE,
117 cur_group: GroupCode::BottomLevel,
118 cur_boundary: 0,
119 }
120 }
121}
122
123impl Engine {
124 pub fn new_save_level(&mut self, c: GroupCode) {
126 let line = self.input.line();
127 let entry = SaveEntry::Boundary {
128 group: self.save.cur_group,
129 boundary: self.save.cur_boundary,
130 line,
131 };
132 if self.save.cur_level == u16::MAX {
133 self.overflow("grouping levels", i64::from(u16::MAX - 1));
134 }
135 self.save.cur_boundary = self.save.stack.len();
136 self.save.stack.push(entry);
137 self.save.cur_group = c;
138 if self.eqtb.int(ip::TRACINGGROUPS) > 0 {
139 self.group_trace(false);
140 }
141 self.save.cur_level += 1;
142 }
143
144 fn eq_save(&mut self, loc: Loc, level: u16) {
146 let old = if level == LEVEL_ZERO {
147 Val::Meaning(Meaning::UNDEFINED)
148 } else {
149 self.eqtb.get(loc)
150 };
151 self.save.stack.push(SaveEntry::Restore { loc, old, level });
152 }
153
154 pub fn eq_define(&mut self, loc: Loc, val: Val) {
156 let tracing = self.eqtb.int(ip::TRACINGASSIGNS) > 0;
157 if !tracing {
160 let cur = self.save.cur_level;
161 match (loc, &val) {
162 (Loc::Code(kind, ch), Val::Int(v)) => {
163 if self.eqtb.code_table_mut(kind).define_in_place(ch, *v, cur) {
164 return;
165 }
166 }
167 (Loc::Count(i), Val::Int(v)) => {
168 let s = &mut self.eqtb.count[i as usize];
169 if i64::from(s.val) == *v {
170 return;
171 }
172 if s.level == cur || cur == LEVEL_ONE {
173 s.val = *v as i32;
174 s.level = cur;
175 return;
176 }
177 }
178 (Loc::Cs(cs), Val::Meaning(m)) if (cs.0 as usize) < self.eqtb.cs.len() => {
179 let s = &self.eqtb.cs[cs.0 as usize];
180 if s.val.same_as(m) {
181 return;
182 }
183 if s.level == cur || cur == LEVEL_ONE {
184 let Val::Meaning(m) = val else { unreachable!() };
185 self.eqtb.cs[cs.0 as usize] = crate::eqtb::Slot { val: m, level: cur };
186 return;
187 }
188 }
189 _ => {}
190 }
191 }
192 if self.eqtb.holds(loc, &val) {
193 if tracing {
194 self.restore_trace(loc, "reassigning");
195 }
196 return;
197 }
198 if tracing {
199 self.restore_trace(loc, "changing");
200 }
201 let cur = self.save.cur_level;
202 let level = self.eqtb.level(loc);
203 if level != cur && cur > LEVEL_ONE {
204 self.eq_save(loc, level);
205 } else if Eqtb::is_word(loc) && level != cur {
206 }
209 self.eqtb.set(loc, val, cur);
210 if tracing {
211 self.restore_trace(loc, "into");
212 }
213 }
214
215 pub fn define_codes_identity(&mut self, kind: CodeKind, upto: u32) {
221 let cur = self.save.cur_level;
222 debug_assert!(cur > LEVEL_ONE);
223 let old = self.eqtb.code_table_mut(kind).define_identity(upto, cur);
224 self.save.stack.push(SaveEntry::RestoreCodes {
225 kind,
226 old: Box::new(old),
227 upto,
228 });
229 }
230
231 pub fn geq_define(&mut self, loc: Loc, val: Val) {
233 let tracing = self.eqtb.int(ip::TRACINGASSIGNS) > 0;
234 if tracing {
235 self.restore_trace(loc, "globally changing");
236 }
237 if self.figures.recording() {
238 self.figures.global(loc);
239 }
240 self.eqtb.set(loc, val, LEVEL_ONE);
241 if tracing {
242 self.restore_trace(loc, "into");
243 }
244 }
245
246 pub fn define(&mut self, global: bool, loc: Loc, val: Val) {
248 if global {
249 self.geq_define(loc, val);
250 } else {
251 self.eq_define(loc, val);
252 }
253 }
254
255 pub fn save_for_after(&mut self, t: Token) {
258 if self.save.cur_level > LEVEL_ONE {
259 self.save.stack.push(SaveEntry::InsertToken(t));
260 }
261 }
262
263 pub fn unsave(&mut self) {
265 if self.save.cur_level <= LEVEL_ONE {
266 self.confusion("curlevel");
267 }
268 self.save.cur_level -= 1;
269 self.drop_text_dirs_above(self.save.cur_level);
270 let tracing = self.eqtb.int(ip::TRACINGRESTORES) > 0;
271 let mut after: Vec<Token> = Vec::new();
274 loop {
275 let Some(entry) = self.save.stack.pop() else {
276 self.confusion("unsave");
277 };
278 match entry {
279 SaveEntry::Boundary {
280 group,
281 boundary,
282 line,
283 } => {
284 if self.eqtb.int(ip::TRACINGGROUPS) > 0 {
285 self.save.stack.push(SaveEntry::Boundary {
287 group,
288 boundary,
289 line,
290 });
291 self.group_trace(true);
292 self.save.stack.pop();
293 }
294 self.group_warning();
295 self.save.cur_group = group;
296 self.save.cur_boundary = boundary;
297 if self.figures.busy() {
298 self.check_figure_end(!after.is_empty());
299 }
300 if !after.is_empty() {
301 after.reverse();
302 self.back_list(std::rc::Rc::from(after));
303 }
304 break;
305 }
306 SaveEntry::InsertToken(t) => after.push(t),
307 SaveEntry::Restore { loc, old, level } => {
308 if self.eqtb.level(loc) == LEVEL_ONE {
309 if let (Loc::Box(n), Val::Node(b)) = (loc, &old)
311 && !b.is_null()
312 && *b != self.eqtb.boxes[n as usize].val
313 {
314 self.nodes.flush_list(*b);
315 }
316 if tracing {
317 self.restore_trace(loc, "retaining");
318 }
319 } else {
320 if let (Loc::Box(n), Val::Node(b)) = (loc, &old) {
322 let cur = self.eqtb.boxes[n as usize].val;
323 if !cur.is_null() && cur != *b {
324 self.nodes.flush_list(cur);
325 }
326 }
327 self.eqtb.set(loc, old, level);
328 if let Loc::IntPar(i) = loc {
331 self.int_param_changed(i);
332 }
333 if tracing {
334 self.restore_trace(loc, "restoring");
335 }
336 }
337 }
338 SaveEntry::Value(_) => {}
339 SaveEntry::RestoreCodes { kind, old, upto } => {
340 self.eqtb.code_table_mut(kind).restore_identity(&old, upto);
341 }
342 }
343 }
344 }
345
346 pub fn group_line(&self) -> i32 {
348 match self.save.stack.get(self.save.cur_boundary) {
349 Some(SaveEntry::Boundary { line, .. }) => *line,
350 _ => 0,
351 }
352 }
353
354 pub fn print_group(&mut self, e: bool) {
356 let p = &mut self.printer;
357 match self.save.cur_group {
358 GroupCode::BottomLevel => {
359 p.print_str("bottom level");
360 return;
361 }
362 GroupCode::Simple | GroupCode::SemiSimple => {
363 if self.save.cur_group == GroupCode::SemiSimple {
364 p.print_str("semi ");
365 }
366 p.print_str("simple");
367 }
368 GroupCode::Hbox | GroupCode::AdjustedHbox => {
369 if self.save.cur_group == GroupCode::AdjustedHbox {
370 p.print_str("adjusted ");
371 }
372 p.print_str("hbox");
373 }
374 GroupCode::Vbox => p.print_str("vbox"),
375 GroupCode::Vtop => p.print_str("vtop"),
376 GroupCode::Align | GroupCode::NoAlign => {
377 if self.save.cur_group == GroupCode::NoAlign {
378 p.print_str("no ");
379 }
380 p.print_str("align");
381 }
382 GroupCode::Output => p.print_str("output"),
383 GroupCode::Disc => p.print_str("disc"),
384 GroupCode::Insert => p.print_str("insert"),
385 GroupCode::Vcenter => p.print_str("vcenter"),
386 GroupCode::Math
387 | GroupCode::MathChoice
388 | GroupCode::MathShift
389 | GroupCode::MathLeft => {
390 p.print_str("math");
391 match self.save.cur_group {
392 GroupCode::MathChoice => p.print_str(" choice"),
393 GroupCode::MathShift => p.print_str(" shift"),
394 GroupCode::MathLeft => p.print_str(" left"),
395 _ => {}
396 }
397 }
398 GroupCode::LocalBox => p.print_str("local box"),
399 GroupCode::SplitOff => p.print_str("split off"),
400 GroupCode::SplitKeep => p.print_str("split keep"),
401 GroupCode::Preamble => p.print_str("preamble"),
402 GroupCode::AlignSet => p.print_str("align set"),
403 GroupCode::FinRow => p.print_str("fin row"),
404 }
405 let level = self.save.cur_level;
406 let line = self.group_line();
407 let p = &mut self.printer;
408 p.print_str(" group (level ");
409 p.print_int(i64::from(level));
410 p.print_char(')' as u32);
411 if line != 0 {
412 p.print_str(if e { " entered at line " } else { " at line " });
413 p.print_int(line.into());
414 }
415 }
416
417 fn group_trace(&mut self, e: bool) {
419 self.begin_diagnostic();
420 self.printer.print_char('{' as u32);
421 self.printer
422 .print_str(if e { "leaving " } else { "entering " });
423 self.print_group(e);
424 self.printer.print_char('}' as u32);
425 self.end_diagnostic(false);
426 }
427
428 pub fn restore_trace(&mut self, loc: Loc, s: &str) {
432 if matches!(loc, Loc::Cat { .. } | Loc::Code(..)) {
433 return;
434 }
435 self.begin_diagnostic();
436 self.printer.print_char('{' as u32);
437 self.printer.print_str(s);
438 self.printer.print_char(' ' as u32);
439 self.show_eqtb(loc);
440 self.printer.print_char('}' as u32);
441 self.end_diagnostic(false);
442 }
443
444 fn group_warning(&mut self) {
447 }
449}