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 self.eqtb.set(loc, val, LEVEL_ONE);
238 if tracing {
239 self.restore_trace(loc, "into");
240 }
241 }
242
243 pub fn define(&mut self, global: bool, loc: Loc, val: Val) {
245 if global {
246 self.geq_define(loc, val);
247 } else {
248 self.eq_define(loc, val);
249 }
250 }
251
252 pub fn save_for_after(&mut self, t: Token) {
255 if self.save.cur_level > LEVEL_ONE {
256 self.save.stack.push(SaveEntry::InsertToken(t));
257 }
258 }
259
260 pub fn unsave(&mut self) {
262 if self.save.cur_level <= LEVEL_ONE {
263 self.confusion("curlevel");
264 }
265 self.save.cur_level -= 1;
266 self.drop_text_dirs_above(self.save.cur_level);
267 let tracing = self.eqtb.int(ip::TRACINGRESTORES) > 0;
268 let mut after: Vec<Token> = Vec::new();
271 loop {
272 let Some(entry) = self.save.stack.pop() else {
273 self.confusion("unsave");
274 };
275 match entry {
276 SaveEntry::Boundary {
277 group,
278 boundary,
279 line,
280 } => {
281 if self.eqtb.int(ip::TRACINGGROUPS) > 0 {
282 self.save.stack.push(SaveEntry::Boundary {
284 group,
285 boundary,
286 line,
287 });
288 self.group_trace(true);
289 self.save.stack.pop();
290 }
291 self.group_warning();
292 self.save.cur_group = group;
293 self.save.cur_boundary = boundary;
294 if !after.is_empty() {
295 after.reverse();
296 self.back_list(std::rc::Rc::from(after));
297 }
298 break;
299 }
300 SaveEntry::InsertToken(t) => after.push(t),
301 SaveEntry::Restore { loc, old, level } => {
302 if self.eqtb.level(loc) == LEVEL_ONE {
303 if let (Loc::Box(n), Val::Node(b)) = (loc, &old)
305 && !b.is_null()
306 && *b != self.eqtb.boxes[n as usize].val
307 {
308 self.nodes.flush_list(*b);
309 }
310 if tracing {
311 self.restore_trace(loc, "retaining");
312 }
313 } else {
314 if let (Loc::Box(n), Val::Node(b)) = (loc, &old) {
316 let cur = self.eqtb.boxes[n as usize].val;
317 if !cur.is_null() && cur != *b {
318 self.nodes.flush_list(cur);
319 }
320 }
321 self.eqtb.set(loc, old, level);
322 if let Loc::IntPar(i) = loc {
325 self.int_param_changed(i);
326 }
327 if tracing {
328 self.restore_trace(loc, "restoring");
329 }
330 }
331 }
332 SaveEntry::Value(_) => {}
333 SaveEntry::RestoreCodes { kind, old, upto } => {
334 self.eqtb.code_table_mut(kind).restore_identity(&old, upto);
335 }
336 }
337 }
338 }
339
340 pub fn group_line(&self) -> i32 {
342 match self.save.stack.get(self.save.cur_boundary) {
343 Some(SaveEntry::Boundary { line, .. }) => *line,
344 _ => 0,
345 }
346 }
347
348 pub fn print_group(&mut self, e: bool) {
350 let p = &mut self.printer;
351 match self.save.cur_group {
352 GroupCode::BottomLevel => {
353 p.print_str("bottom level");
354 return;
355 }
356 GroupCode::Simple | GroupCode::SemiSimple => {
357 if self.save.cur_group == GroupCode::SemiSimple {
358 p.print_str("semi ");
359 }
360 p.print_str("simple");
361 }
362 GroupCode::Hbox | GroupCode::AdjustedHbox => {
363 if self.save.cur_group == GroupCode::AdjustedHbox {
364 p.print_str("adjusted ");
365 }
366 p.print_str("hbox");
367 }
368 GroupCode::Vbox => p.print_str("vbox"),
369 GroupCode::Vtop => p.print_str("vtop"),
370 GroupCode::Align | GroupCode::NoAlign => {
371 if self.save.cur_group == GroupCode::NoAlign {
372 p.print_str("no ");
373 }
374 p.print_str("align");
375 }
376 GroupCode::Output => p.print_str("output"),
377 GroupCode::Disc => p.print_str("disc"),
378 GroupCode::Insert => p.print_str("insert"),
379 GroupCode::Vcenter => p.print_str("vcenter"),
380 GroupCode::Math
381 | GroupCode::MathChoice
382 | GroupCode::MathShift
383 | GroupCode::MathLeft => {
384 p.print_str("math");
385 match self.save.cur_group {
386 GroupCode::MathChoice => p.print_str(" choice"),
387 GroupCode::MathShift => p.print_str(" shift"),
388 GroupCode::MathLeft => p.print_str(" left"),
389 _ => {}
390 }
391 }
392 GroupCode::LocalBox => p.print_str("local box"),
393 GroupCode::SplitOff => p.print_str("split off"),
394 GroupCode::SplitKeep => p.print_str("split keep"),
395 GroupCode::Preamble => p.print_str("preamble"),
396 GroupCode::AlignSet => p.print_str("align set"),
397 GroupCode::FinRow => p.print_str("fin row"),
398 }
399 let level = self.save.cur_level;
400 let line = self.group_line();
401 let p = &mut self.printer;
402 p.print_str(" group (level ");
403 p.print_int(i64::from(level));
404 p.print_char(')' as u32);
405 if line != 0 {
406 p.print_str(if e { " entered at line " } else { " at line " });
407 p.print_int(line.into());
408 }
409 }
410
411 fn group_trace(&mut self, e: bool) {
413 self.begin_diagnostic();
414 self.printer.print_char('{' as u32);
415 self.printer
416 .print_str(if e { "leaving " } else { "entering " });
417 self.print_group(e);
418 self.printer.print_char('}' as u32);
419 self.end_diagnostic(false);
420 }
421
422 pub fn restore_trace(&mut self, loc: Loc, s: &str) {
426 if matches!(loc, Loc::Cat { .. } | Loc::Code(..)) {
427 return;
428 }
429 self.begin_diagnostic();
430 self.printer.print_char('{' as u32);
431 self.printer.print_str(s);
432 self.printer.print_char(' ' as u32);
433 self.show_eqtb(loc);
434 self.printer.print_char('}' as u32);
435 self.end_diagnostic(false);
436 }
437
438 fn group_warning(&mut self) {
441 }
443}