texlang_stdlib/
alias.rs

1//! `\let` aliasing command
2
3use crate::prefix;
4use texlang::parse::{Command, OptionalEqualsUnexpanded};
5use texlang::traits::*;
6use texlang::*;
7
8pub const LET_DOC: &str = "Assign a command or character to a control sequence";
9
10/// Get the `\let` command.
11pub fn get_let<S: HasComponent<prefix::Component>>() -> command::BuiltIn<S> {
12    command::BuiltIn::new_execution(let_primitive_fn)
13        .with_tag(let_tag())
14        .with_doc(LET_DOC)
15}
16
17static LET_TAG: command::StaticTag = command::StaticTag::new();
18
19pub fn let_tag() -> command::Tag {
20    LET_TAG.get()
21}
22
23fn let_primitive_fn<S: HasComponent<prefix::Component>>(
24    _: token::Token,
25    input: &mut vm::ExecutionInput<S>,
26) -> Result<(), Box<error::Error>> {
27    let scope = input.state_mut().component_mut().read_and_reset_global();
28    let Command::ControlSequence(alias) = Command::parse(input)?;
29    OptionalEqualsUnexpanded::parse(input)?;
30    match input.unexpanded().next()? {
31        None => Err(error::SimpleEndOfInputError::new(
32            input.vm(),
33            "unexpected end of input while reading the right hand side of a \\let assignment",
34        )
35        .into()),
36        Some(token) => match token.value() {
37            token::Value::ControlSequence(control_sequence) => {
38                match input.commands_map_mut().alias_control_sequence(
39                    alias,
40                    control_sequence,
41                    scope,
42                ) {
43                    Ok(()) => Ok(()),
44                    Err(_) => Err(error::UndefinedCommandError::new(input.vm(), token).into()),
45                }
46            }
47            _ => {
48                input.commands_map_mut().alias_token(alias, token, scope);
49                Ok(())
50            }
51        },
52    }
53}
54
55#[cfg(test)]
56mod test {
57    use std::collections::HashMap;
58
59    use super::*;
60    use crate::def;
61    use crate::testing::*;
62    use crate::the;
63
64    fn initial_commands() -> HashMap<&'static str, command::BuiltIn<State>> {
65        HashMap::from([
66            ("def", def::get_def()),
67            ("global", prefix::get_global()),
68            ("integer", State::get_integer()),
69            ("let", get_let()),
70            ("the", the::get_the()),
71        ])
72    }
73
74    test_suite![
75        expansion_equality_tests(
76            (let_for_macro, r"\def\A{abc}\let\B\A\B", "abc"),
77            (local, r"\def\A{a}\def\B{b}\let\C=\A{\let\C=\B \C}\C", "ba"),
78            (
79                global,
80                r"\def\A{a}\def\B{b}\let\C=\A{\global\let\C=\B \C}\C",
81                "bb"
82            ),
83            (let_for_macro_equals, r"\def\A{abc}\let\B=\A\B", "abc"),
84            (let_character, r"\let\A=B\A", "B"),
85        ),
86        serde_tests(
87            (
88                alias_execution_primitive,
89                r"\let\defNew=\def",
90                r"\defNew\A{abc}\A"
91            ),
92            (
93                alias_expansion_primitive,
94                r"\let\theNew=\the",
95                r"\integer=3\theNew\integer"
96            ),
97            (
98                alias_variable_singleton,
99                r"\let\integerNew=\integer",
100                r"\integerNew=3\the\integer"
101            ),
102            (serde_macro, r"\def\A{Hello World}\let\B=\A ", r"\A \B",),
103            (serde_character, r"\let\A=B ", r"\A",),
104        ),
105        failure_tests((let_unknown_cs_name, r"\let \B=\A")),
106    ];
107}