1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
use crate::prelude as txl;
use crate::traits::*;
use crate::*;

/// Parses an arithmetic variable (integer, glue, dimension, etc).
pub struct ArithmeticVariable<S>(pub Option<variable::Variable<S>>);

impl<S: TexlangState> Parsable<S> for ArithmeticVariable<S> {
    fn parse_impl(input: &mut vm::ExpandedStream<S>) -> txl::Result<Self> {
        let token = input.next(ArithmeticVariableEndOfInput {})?;
        match token.value() {
            token::Value::CommandRef(command_ref) => {
                match input.commands_map().get_command(&command_ref) {
                    None => {
                        input.vm().error(
                            parse::Error::new("a variable", Some(token), "")
                                .with_got_override("got an undefined control sequence")
                                .with_annotation_override("undefined control sequence"),
                        )?;
                        Ok(ArithmeticVariable(None))
                    }
                    Some(command::Command::Variable(cmd)) => {
                        if !cmd.is_arithmetic() {
                            input.vm().error(
                                parse::Error::new("an arithmetic variable", Some(token), "")
                                    .with_got_override("got a non-arithmetic variable"),
                            )?;
                            Ok(ArithmeticVariable(None))
                        } else {
                            Ok(ArithmeticVariable(Some(cmd.clone().resolve(token, input)?)))
                        }
                    }
                    Some(cmd) => {
                        input.vm().error(
                            parse::Error::new("a variable", Some(token), "")
                                .with_got_override("got a non-variable command")
                                .with_annotation_override(format![
                                    "control sequence referencing {cmd}"
                                ]),
                        )?;
                        Ok(ArithmeticVariable(None))
                    }
                }
            }
            _ => {
                input.vm().error(
                    parse::Error::new("a variable", Some(token), "")
                        .with_got_override("got a character token"),
                )?;
                Ok(ArithmeticVariable(None))
            }
        }
    }
}

#[derive(Debug)]
struct ArithmeticVariableEndOfInput;

impl error::EndOfInputError for ArithmeticVariableEndOfInput {
    fn doing(&self) -> String {
        "parsing an arithmetic variable".into()
    }
}

/// When parsed, this type consumes an optional equals from the token stream.
pub struct OptionalEquals;

impl<S: TexlangState> Parsable<S> for OptionalEquals {
    fn parse_impl(input: &mut vm::ExpandedStream<S>) -> txl::Result<Self> {
        // scan_optional_equals
        parse_optional_equals(input)?;
        Ok(OptionalEquals {})
    }
}

/// When parsed, this type consumes an optional equals from the token stream without performing expansion.
pub struct OptionalEqualsUnexpanded;

impl<S: TexlangState> Parsable<S> for OptionalEqualsUnexpanded {
    fn parse_impl(input: &mut vm::ExpandedStream<S>) -> txl::Result<Self> {
        parse_optional_equals(input.unexpanded())?;
        Ok(OptionalEqualsUnexpanded {})
    }
}

// Corresponds to the `scan_optional_equals` procedure in Knuth's TeX (405)
fn parse_optional_equals<S: TexlangState, I: TokenStream<S = S>>(input: &mut I) -> txl::Result<()> {
    while let Some(found_equals) = get_optional_element![
        input,
        token::Value::Other('=') => true,
        token::Value::Space(_) => false,
    ] {
        if found_equals {
            break;
        }
    }
    // TODO: this is not correct: this function should not scan spaces after the equals.
    // A separate routine corresponding to Knuth TeX 404 needs to be added and used instead
    // at the right call sites.
    while get_optional_element![
        input,
        token::Value::Space(_) => (),
    ]
    .is_some()
    {}
    Ok(())
}