1use 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
10pub 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}