1use std::{borrow::Cow, cell::RefCell, fmt, rc::Rc};
6
7use libxml::tree::Node;
8
9use crate::Digested;
11use crate::{
12 common::{
13 error::{emit_warn, *},
14 locator::Locator,
15 object::Object,
16 },
17 definition::{BeforeDigestClosure, ConditionalClosure, Definition, DigestionClosure},
18 document::Document,
19 gullet,
20 parameter::Parameters,
21 pin,
22 state::*,
23 token::*,
24 tokens::Tokens,
25 whatsit::Whatsit,
26};
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum ConditionalType {
36 If,
38 Unless,
40 Else,
42 Or,
44 Fi,
46 Unknown,
48}
49
50impl From<&str> for ConditionalType {
51 fn from(cs: &str) -> Self {
52 use self::ConditionalType::*;
53 match cs {
54 "\\if" => If,
55 "\\unless" => Unless,
56 "\\else" => Else,
57 "\\or" => Or,
58 "\\fi" => Fi,
59 _ => If,
60 }
61 }
62}
63
64#[derive(Default)]
66pub struct ConditionalOptions {
67 pub scope: Option<Scope>,
69 pub locked: Option<bool>,
71 pub skipper: Option<bool>,
74}
75
76#[derive(Clone)]
78pub struct Conditional {
79 pub cs: Token,
81 pub paramlist: Option<Parameters>,
83 pub test: Option<ConditionalClosure>,
85 pub conditional_type: ConditionalType,
87 pub skipper: Option<bool>,
89}
90impl Default for Conditional {
91 fn default() -> Self {
92 Conditional {
93 cs: T_CS!("Conditional"),
94 paramlist: None,
95 test: None,
96 conditional_type: ConditionalType::Unknown,
97 skipper: None,
98 }
99 }
100}
101impl PartialEq for Conditional {
102 fn eq(&self, other: &Conditional) -> bool { self.cs == other.cs }
103}
104
105impl fmt::Display for Conditional {
106 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.cs) }
107}
108impl Object for Conditional {
109 fn is_expandable(&self) -> bool { true }
110 fn stringify(&self) -> String { self.stringify_type("Conditional") }
111}
112impl Definition for Conditional {
113 fn invoke(&self, _once_only: bool) -> Result<Tokens> {
124 use self::ConditionalType::*;
126 match self.conditional_type {
127 If | Unless => self.invoke_conditional(),
128 Else | Or => self.invoke_else(),
129 Fi => self.invoke_fi(),
130 _ => {
131 let cur = get_current_token()
135 .map(|t| t.stringify())
136 .unwrap_or_else(|| String::from("\\?"));
137 let message = s!("Unknown conditional control sequence {}", cur);
138 Error!("unexpected", self.cs, message);
139 Ok(Tokens!())
140 },
141 }
142 }
143
144 fn get_parameters(&self) -> Option<&Parameters> { self.paramlist.as_ref() }
145 fn get_cs(&self) -> Cow<'_, Token> { Cow::Borrowed(&self.cs) }
146 fn get_cs_name(&self) -> Cow<'_, str> { Cow::Owned(self.cs.with_cs_name(ToString::to_string)) }
147 fn get_alias(&self) -> Option<&String> { None }
148 fn get_test(&self) -> Option<&ConditionalClosure> { self.test.as_ref() }
149 fn get_conditional_type(&self) -> Option<ConditionalType> { Some(self.conditional_type) }
150 fn invoke_primitive(&self) -> Result<Vec<Digested>> {
152 Ok(Vec::new())
154 }
155 fn before_digest(&self) -> Option<&Vec<BeforeDigestClosure>> { None }
156 fn after_digest(&self) -> Option<&Vec<DigestionClosure>> { None }
157 fn do_absorption(&self, _document: &mut Document, _whatsit: &Whatsit) -> Result<Vec<Node>> {
158 fatal!(
159 Definition,
160 Unexpected,
161 "do_absorption on Conditional should never be called!"
162 );
163 }
164}
165
166#[derive(Debug, Clone, PartialEq)]
168pub struct IfFrame {
169 pub token: Token,
171 pub start: Locator,
173 pub parsing: bool,
175 pub elses: bool,
177 pub ifid: i64,
179}
180
181impl Conditional {
182 fn invoke_conditional(&self) -> Result<Tokens> {
183 let mut ifid = lookup_int_sym(pin!("if_count"));
187 ifid += 1;
188 assign_value_sym(pin!("if_count"), ifid, Some(Scope::Global));
189 let if_limit = lookup_int_sym(pin!("if_limit"));
191 if if_limit > 0 && ifid > if_limit {
192 Fatal!(
193 Timeout,
194 IfLimit,
195 s!("Conditional limit of {} exceeded, infinite loop?", if_limit)
196 );
197 }
198 let if_frame = Rc::new(RefCell::new(IfFrame {
199 token: get_current_token().unwrap(),
200 start: gullet::get_locator(),
201 parsing: true,
202 elses: false,
203 ifid,
204 }));
205 set_ifframe(Some(Rc::clone(&if_frame)));
206 unshift_value("if_stack", vec![Rc::clone(&if_frame)]);
207 let args = self.read_arguments()?;
208
209 get_ifframe().unwrap().borrow_mut().parsing = false;
210 if let Some(ref test) = self.test {
215 if (test)(args)? {
216 } else {
218 let to = self.skip_conditional_body(-1);
219 if lookup_bool_sym(pin!("tracingcommands")) {
220 Debug!("{{false}} [skipped to {:?}]\n", to);
221 }
222 }
223 } else {
224 let num = args.first().map(|a| a.value_of()).unwrap_or(0);
228 if num != 0 {
229 let _to = self.skip_conditional_body(num);
230 }
232 }
233 expire_ifframe();
234 Ok(Tokens!())
235 }
236
237 fn skip_conditional_body(&self, nskips: i64) -> Result<Tokens> {
262 let mut level = 1;
263 let mut n_ors = 0;
264 let _start = gullet::get_locator();
265 loop {
268 let (t, cond_type) = match gullet::read_next_conditional()? {
269 Some((tok, typ)) => (Tokens!(tok), Some(typ)),
270 None => (Tokens!(), None),
271 };
272 match cond_type {
273 None => break,
274 Some(ConditionalType::If) => level += 1, Some(ConditionalType::Fi) => {
276 let local_frame = get_ifframe();
278 let maybe_last = with_value_mut("if_stack", |value_opt| {
279 if let Some(Stored::VecDequeStored(stack)) = value_opt
280 && let Some(Stored::IfFrame(stack_frame)) = stack.pop_front()
281 {
282 if *stack_frame.borrow() != *local_frame.as_ref().unwrap().borrow() {
283 } else {
286 level -= 1;
287 if level == 0 {
288 return Some(t); } else {
292 stack.push_front(stack_frame.into());
293 }
294 }
295 }
296 None
297 });
298 if let Some(t) = maybe_last {
299 return Ok(t);
300 }
301 },
302 Some(other_type) => {
303 if level > 1 {
304 } else if other_type == ConditionalType::Or {
306 n_ors += 1;
307 if n_ors == nskips {
308 return Ok(t);
309 }
310 } else if other_type == ConditionalType::Else && nskips != 0 {
311 let local_frame = get_ifframe();
313 let maybe_last = with_value("if_stack", |stack_opt| {
315 if let Some(Stored::VecDequeStored(stack)) = stack_opt
316 && let Some(Stored::IfFrame(stack_frame)) = stack.front()
317 && *stack_frame.borrow() == *local_frame.as_ref().unwrap().borrow()
318 {
319 stack_frame.borrow_mut().elses = true;
321 return Some(t);
322 }
323 None
324 });
325 if let Some(t) = maybe_last {
326 return Ok(t);
327 }
328 }
329 },
330 };
331 }
332 Error!(
333 "expected",
334 "\\fi",
335 self,
336 s!(
337 "Missing \\fi or \\else, conditional fell off end. Conditional started at {:?}",
338 _start
339 )
340 );
341 Ok(Tokens!())
342 }
343
344 fn invoke_else(&self) -> Result<Tokens> {
345 let stack_frame_opt = with_value_mut("if_stack", |stack_opt| {
346 if let Some(Stored::VecDequeStored(stack)) = stack_opt {
347 if let Some(Stored::IfFrame(stack_frame)) = stack.front() {
348 Some(Rc::clone(stack_frame))
349 } else {
350 None
351 }
352 } else {
353 None
354 }
355 });
356 let local_token = get_current_token().unwrap();
357 if local_token.with_str(|s| s == "\\else") && stack_frame_opt.is_none() {
358 let stack_len = with_value("if_stack", |v| match v {
359 Some(Stored::VecDequeStored(s)) => s.len(),
360 _ => 0,
361 });
362 emit_warn(
363 "unexpected",
364 "else",
365 &format!("\\else encountered with no active if-frame (stack_len={stack_len})"),
366 );
367 }
368 if let Some(stack_frame) = stack_frame_opt {
369 if stack_frame.borrow().parsing {
370 Ok(Tokens!(T_RELAX!(), local_token))
372 } else if stack_frame.borrow().elses {
373 let message = s!(
375 "Extra {} already saw \\else for {:?} [{:?}] at {:?}",
376 local_token.stringify(),
377 stack_frame.borrow().token,
378 stack_frame.borrow().ifid,
379 stack_frame.borrow().start
380 );
381 let local_token_str = local_token.to_string();
382 Error!("unexpected", local_token_str, message);
383 Ok(Tokens!())
384 } else {
385 set_ifframe(Some(Rc::clone(&stack_frame)));
386 let _t = self.skip_conditional_body(0);
387 expire_ifframe();
392 Ok(Tokens!())
393 }
394 } else {
395 let message = s!(
397 "Didn't expect a {:?} since we seem not to be in a conditional",
398 local_token.stringify()
399 );
400 let local_token_str = local_token.to_string();
401 Error!("unexpected", local_token_str, message);
402 Ok(Tokens!())
403 }
404 }
405
406 fn invoke_fi(&self) -> Result<Tokens> {
407 let stack_frame_opt: Option<Rc<RefCell<IfFrame>>> = with_value("if_stack", |stack_opt| {
408 if let Some(Stored::VecDequeStored(stack)) = stack_opt {
409 if let Some(Stored::IfFrame(frame)) = stack.front() {
410 Some(Rc::clone(frame))
411 } else {
412 None
413 }
414 } else {
415 None
416 }
417 });
418 if let Some(stack_frame) = stack_frame_opt {
419 if stack_frame.borrow().parsing {
420 Ok(Tokens!(T_RELAX!(), get_current_token().unwrap()))
422 } else {
423 set_ifframe(Some(stack_frame));
425 shift_value("if_stack")?; expire_ifframe();
431 Ok(Tokens!())
432 }
433 } else {
434 let cur = get_current_token()
435 .map(|t| t.stringify())
436 .unwrap_or_else(|| String::from("\\?"));
437 let message = s!(
438 "Didn't expect a {:?} since we seem not to be in a conditional",
439 cur
440 );
441 Error!("unexpected", "fi", message);
442 Ok(Tokens!())
443 }
444 }
445}
446
447#[cfg(test)]
448mod tests {
449 use super::*;
450
451 #[test]
452 fn conditional_type_from_str_known_variants() {
453 assert_eq!(ConditionalType::from("\\if"), ConditionalType::If);
454 assert_eq!(ConditionalType::from("\\unless"), ConditionalType::Unless);
455 assert_eq!(ConditionalType::from("\\else"), ConditionalType::Else);
456 assert_eq!(ConditionalType::from("\\or"), ConditionalType::Or);
457 assert_eq!(ConditionalType::from("\\fi"), ConditionalType::Fi);
458 }
459
460 #[test]
461 fn conditional_type_from_str_unknown_falls_back_to_if() {
462 assert_eq!(ConditionalType::from("\\foo"), ConditionalType::If);
465 assert_eq!(ConditionalType::from(""), ConditionalType::If);
466 }
467
468 #[test]
469 fn conditional_type_equality() {
470 assert_eq!(ConditionalType::If, ConditionalType::If);
471 assert_ne!(ConditionalType::If, ConditionalType::Else);
472 }
473
474 #[test]
475 fn conditional_default_fields() {
476 let c = Conditional::default();
477 assert!(c.paramlist.is_none());
478 assert!(c.test.is_none());
479 assert_eq!(c.conditional_type, ConditionalType::Unknown);
480 assert!(c.skipper.is_none());
481 }
482
483 #[test]
484 fn conditional_partial_eq_by_cs() {
485 let a = Conditional::default();
487 let b = Conditional::default();
488 assert!(a == b, "defaults have same cs");
489 }
490
491 #[test]
492 fn conditional_is_expandable() {
493 let c = Conditional::default();
494 assert!(c.is_expandable());
495 }
499
500 #[test]
501 fn conditional_display_is_cs_text() {
502 let c = Conditional::default();
503 let s = format!("{c}");
504 assert_eq!(s, "Conditional");
505 }
506
507 #[test]
508 fn conditional_get_parameters_none_by_default() {
509 let c = Conditional::default();
510 assert!(c.get_parameters().is_none());
511 }
512
513 #[test]
514 fn conditional_options_default_all_none() {
515 let o = ConditionalOptions::default();
516 assert!(o.scope.is_none());
517 assert!(o.locked.is_none());
518 assert!(o.skipper.is_none());
519 }
520}