1use crate::Engine;
5use crate::eqtb::{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}
87
88#[derive(Debug, Clone)]
90pub struct SaveStack {
91 pub stack: Vec<SaveEntry>,
93 pub cur_level: u16,
95 pub cur_group: GroupCode,
97 pub cur_boundary: usize,
99}
100
101impl Default for SaveStack {
102 fn default() -> Self {
103 SaveStack {
104 stack: Vec::new(),
105 cur_level: LEVEL_ONE,
106 cur_group: GroupCode::BottomLevel,
107 cur_boundary: 0,
108 }
109 }
110}
111
112impl Engine {
113 pub fn new_save_level(&mut self, c: GroupCode) {
115 let line = self.input.line();
116 let entry = SaveEntry::Boundary {
117 group: self.save.cur_group,
118 boundary: self.save.cur_boundary,
119 line,
120 };
121 if self.save.cur_level == u16::MAX {
122 self.overflow("grouping levels", i64::from(u16::MAX - 1));
123 }
124 self.save.cur_boundary = self.save.stack.len();
125 self.save.stack.push(entry);
126 self.save.cur_group = c;
127 if self.eqtb.int(ip::TRACINGGROUPS) > 0 {
128 self.group_trace(false);
129 }
130 self.save.cur_level += 1;
131 }
132
133 fn eq_save(&mut self, loc: Loc, level: u16) {
135 let old = if level == LEVEL_ZERO {
136 Val::Meaning(Meaning::UNDEFINED)
137 } else {
138 self.eqtb.get(loc)
139 };
140 self.save.stack.push(SaveEntry::Restore { loc, old, level });
141 }
142
143 pub fn eq_define(&mut self, loc: Loc, val: Val) {
145 let tracing = self.eqtb.int(ip::TRACINGASSIGNS) > 0;
146 if !tracing {
149 let cur = self.save.cur_level;
150 match (loc, &val) {
151 (Loc::Code(kind, ch), Val::Int(v)) => {
152 if self.eqtb.code_table_mut(kind).define_in_place(ch, *v, cur) {
153 return;
154 }
155 }
156 (Loc::Count(i), Val::Int(v)) => {
157 let s = &mut self.eqtb.count[i as usize];
158 if i64::from(s.val) == *v {
159 return;
160 }
161 if s.level == cur || cur == LEVEL_ONE {
162 s.val = *v as i32;
163 s.level = cur;
164 return;
165 }
166 }
167 (Loc::Cs(cs), Val::Meaning(m)) if (cs.0 as usize) < self.eqtb.cs.len() => {
168 let s = &self.eqtb.cs[cs.0 as usize];
169 if s.val.same_as(m) {
170 return;
171 }
172 if s.level == cur || cur == LEVEL_ONE {
173 let Val::Meaning(m) = val else { unreachable!() };
174 self.eqtb.cs[cs.0 as usize] = crate::eqtb::Slot { val: m, level: cur };
175 return;
176 }
177 }
178 _ => {}
179 }
180 }
181 if self.eqtb.holds(loc, &val) {
182 if tracing {
183 self.restore_trace(loc, "reassigning");
184 }
185 return;
186 }
187 if tracing {
188 self.restore_trace(loc, "changing");
189 }
190 let cur = self.save.cur_level;
191 let level = self.eqtb.level(loc);
192 if level != cur && cur > LEVEL_ONE {
193 self.eq_save(loc, level);
194 } else if Eqtb::is_word(loc) && level != cur {
195 }
198 self.eqtb.set(loc, val, cur);
199 if tracing {
200 self.restore_trace(loc, "into");
201 }
202 }
203
204 pub fn geq_define(&mut self, loc: Loc, val: Val) {
206 let tracing = self.eqtb.int(ip::TRACINGASSIGNS) > 0;
207 if tracing {
208 self.restore_trace(loc, "globally changing");
209 }
210 self.eqtb.set(loc, val, LEVEL_ONE);
211 if tracing {
212 self.restore_trace(loc, "into");
213 }
214 }
215
216 pub fn define(&mut self, global: bool, loc: Loc, val: Val) {
218 if global {
219 self.geq_define(loc, val);
220 } else {
221 self.eq_define(loc, val);
222 }
223 }
224
225 pub fn save_for_after(&mut self, t: Token) {
228 if self.save.cur_level > LEVEL_ONE {
229 self.save.stack.push(SaveEntry::InsertToken(t));
230 }
231 }
232
233 pub fn unsave(&mut self) {
235 if self.save.cur_level <= LEVEL_ONE {
236 self.confusion("curlevel");
237 }
238 self.save.cur_level -= 1;
239 self.drop_text_dirs_above(self.save.cur_level);
240 let tracing = self.eqtb.int(ip::TRACINGRESTORES) > 0;
241 let mut after: Vec<Token> = Vec::new();
244 loop {
245 let Some(entry) = self.save.stack.pop() else {
246 self.confusion("unsave");
247 };
248 match entry {
249 SaveEntry::Boundary {
250 group,
251 boundary,
252 line,
253 } => {
254 if self.eqtb.int(ip::TRACINGGROUPS) > 0 {
255 self.save.stack.push(SaveEntry::Boundary {
257 group,
258 boundary,
259 line,
260 });
261 self.group_trace(true);
262 self.save.stack.pop();
263 }
264 self.group_warning();
265 self.save.cur_group = group;
266 self.save.cur_boundary = boundary;
267 if !after.is_empty() {
268 after.reverse();
269 self.back_list(std::rc::Rc::from(after));
270 }
271 break;
272 }
273 SaveEntry::InsertToken(t) => after.push(t),
274 SaveEntry::Restore { loc, old, level } => {
275 if self.eqtb.level(loc) == LEVEL_ONE {
276 if let (Loc::Box(n), Val::Node(b)) = (loc, &old)
278 && !b.is_null()
279 && *b != self.eqtb.boxes[n as usize].val
280 {
281 self.nodes.flush_list(*b);
282 }
283 if tracing {
284 self.restore_trace(loc, "retaining");
285 }
286 } else {
287 if let (Loc::Box(n), Val::Node(b)) = (loc, &old) {
289 let cur = self.eqtb.boxes[n as usize].val;
290 if !cur.is_null() && cur != *b {
291 self.nodes.flush_list(cur);
292 }
293 }
294 self.eqtb.set(loc, old, level);
295 if let Loc::IntPar(i) = loc {
298 self.int_param_changed(i);
299 }
300 if tracing {
301 self.restore_trace(loc, "restoring");
302 }
303 }
304 }
305 SaveEntry::Value(_) => {}
306 }
307 }
308 }
309
310 pub fn group_line(&self) -> i32 {
312 match self.save.stack.get(self.save.cur_boundary) {
313 Some(SaveEntry::Boundary { line, .. }) => *line,
314 _ => 0,
315 }
316 }
317
318 pub fn print_group(&mut self, e: bool) {
320 let p = &mut self.printer;
321 match self.save.cur_group {
322 GroupCode::BottomLevel => {
323 p.print_str("bottom level");
324 return;
325 }
326 GroupCode::Simple | GroupCode::SemiSimple => {
327 if self.save.cur_group == GroupCode::SemiSimple {
328 p.print_str("semi ");
329 }
330 p.print_str("simple");
331 }
332 GroupCode::Hbox | GroupCode::AdjustedHbox => {
333 if self.save.cur_group == GroupCode::AdjustedHbox {
334 p.print_str("adjusted ");
335 }
336 p.print_str("hbox");
337 }
338 GroupCode::Vbox => p.print_str("vbox"),
339 GroupCode::Vtop => p.print_str("vtop"),
340 GroupCode::Align | GroupCode::NoAlign => {
341 if self.save.cur_group == GroupCode::NoAlign {
342 p.print_str("no ");
343 }
344 p.print_str("align");
345 }
346 GroupCode::Output => p.print_str("output"),
347 GroupCode::Disc => p.print_str("disc"),
348 GroupCode::Insert => p.print_str("insert"),
349 GroupCode::Vcenter => p.print_str("vcenter"),
350 GroupCode::Math
351 | GroupCode::MathChoice
352 | GroupCode::MathShift
353 | GroupCode::MathLeft => {
354 p.print_str("math");
355 match self.save.cur_group {
356 GroupCode::MathChoice => p.print_str(" choice"),
357 GroupCode::MathShift => p.print_str(" shift"),
358 GroupCode::MathLeft => p.print_str(" left"),
359 _ => {}
360 }
361 }
362 GroupCode::LocalBox => p.print_str("local box"),
363 GroupCode::SplitOff => p.print_str("split off"),
364 GroupCode::SplitKeep => p.print_str("split keep"),
365 GroupCode::Preamble => p.print_str("preamble"),
366 GroupCode::AlignSet => p.print_str("align set"),
367 GroupCode::FinRow => p.print_str("fin row"),
368 }
369 let level = self.save.cur_level;
370 let line = self.group_line();
371 let p = &mut self.printer;
372 p.print_str(" group (level ");
373 p.print_int(i64::from(level));
374 p.print_char(')' as u32);
375 if line != 0 {
376 p.print_str(if e { " entered at line " } else { " at line " });
377 p.print_int(line.into());
378 }
379 }
380
381 fn group_trace(&mut self, e: bool) {
383 self.begin_diagnostic();
384 self.printer.print_char('{' as u32);
385 self.printer
386 .print_str(if e { "leaving " } else { "entering " });
387 self.print_group(e);
388 self.printer.print_char('}' as u32);
389 self.end_diagnostic(false);
390 }
391
392 pub fn restore_trace(&mut self, loc: Loc, s: &str) {
396 if matches!(loc, Loc::Cat { .. } | Loc::Code(..)) {
397 return;
398 }
399 self.begin_diagnostic();
400 self.printer.print_char('{' as u32);
401 self.printer.print_str(s);
402 self.printer.print_char(' ' as u32);
403 self.show_eqtb(loc);
404 self.printer.print_char('}' as u32);
405 self.end_diagnostic(false);
406 }
407
408 fn group_warning(&mut self) {
411 }
413}