1use std::collections::HashMap;
2
3use crate::error::{ParseError, ParseResult};
4use crate::functions::{define_function_full, FunctionContext, FunctionSpec};
5use crate::macro_expander::MacroDefinition;
6use crate::parse_node::ParseNode;
7
8static GLOBAL_MAP: &[(&str, &str)] = &[
9 ("\\global", "\\global"),
10 ("\\long", "\\\\globallong"),
11 ("\\\\globallong", "\\\\globallong"),
12 ("\\def", "\\gdef"),
13 ("\\gdef", "\\gdef"),
14 ("\\edef", "\\xdef"),
15 ("\\xdef", "\\xdef"),
16 ("\\let", "\\\\globallet"),
17 ("\\futurelet", "\\\\globalfuture"),
18];
19
20fn global_version(name: &str) -> Option<&'static str> {
21 GLOBAL_MAP.iter().find(|(k, _)| *k == name).map(|(_, v)| *v)
22}
23
24fn check_control_sequence(text: &str) -> ParseResult<()> {
25 if matches!(text, "\\" | "{" | "}" | "$" | "&" | "#" | "^" | "_" | "EOF") {
26 return Err(ParseError::msg("Expected a control sequence"));
27 }
28 Ok(())
29}
30
31pub fn register(map: &mut HashMap<&'static str, FunctionSpec>) {
32 define_function_full(
34 map,
35 &["\\global", "\\long", "\\\\globallong"],
36 "internal",
37 0, 0, None,
38 false,
39 true, true,
40 false, false,
41 handle_prefix,
42 );
43
44 define_function_full(
46 map,
47 &["\\def", "\\gdef", "\\edef", "\\xdef"],
48 "internal",
49 0, 0, None,
50 false,
51 true, true,
52 false, true,
53 handle_def,
54 );
55
56 define_function_full(
58 map,
59 &["\\let", "\\\\globallet"],
60 "internal",
61 0, 0, None,
62 false,
63 true, true,
64 false, true,
65 handle_let,
66 );
67
68 define_function_full(
70 map,
71 &["\\futurelet", "\\\\globalfuture"],
72 "internal",
73 0, 0, None,
74 false,
75 true, true,
76 false, true,
77 handle_futurelet,
78 );
79}
80
81fn handle_prefix(
82 ctx: &mut FunctionContext,
83 _args: Vec<ParseNode>,
84 _opt_args: Vec<Option<ParseNode>>,
85) -> ParseResult<ParseNode> {
86 ctx.parser.consume_spaces()?;
87 let mut next_tok = ctx.parser.fetch()?.clone();
88 let token_text = next_tok.text.clone();
89
90 if let Some(global_ver) = global_version(&token_text) {
91 if ctx.func_name == "\\global" || ctx.func_name == "\\\\globallong" {
92 next_tok.text = global_ver.to_string();
93 ctx.parser.gullet.push_token(next_tok);
94 ctx.parser.consume();
95 }
96 let result = ctx.parser.parse_function(None, None)?;
97 match result {
98 Some(node) => Ok(node),
99 None => Err(ParseError::msg("Invalid token after macro prefix")),
100 }
101 } else {
102 Err(ParseError::msg("Invalid token after macro prefix"))
103 }
104}
105
106fn handle_def(
107 ctx: &mut FunctionContext,
108 _args: Vec<ParseNode>,
109 _opt_args: Vec<Option<ParseNode>>,
110) -> ParseResult<ParseNode> {
111 let name_tok = ctx.parser.gullet.pop_token();
112 let name = name_tok.text.clone();
113 check_control_sequence(&name)?;
114
115 let mut num_args = 0usize;
116 let mut insert_brace = false;
117
118 while ctx.parser.gullet.future().text != "{" {
120 let tok = ctx.parser.gullet.pop_token();
121 if tok.text == "#" {
122 if ctx.parser.gullet.future().text == "{" {
123 insert_brace = true;
124 break;
125 }
126 let num_tok = ctx.parser.gullet.pop_token();
127 let n: usize = num_tok.text.parse().map_err(|_| {
128 ParseError::msg(format!("Invalid argument number \"{}\"", num_tok.text))
129 })?;
130 if n != num_args + 1 {
131 return Err(ParseError::msg(format!(
132 "Argument number \"{}\" out of order",
133 n
134 )));
135 }
136 num_args += 1;
137 } else if tok.is_eof() {
138 return Err(ParseError::msg("Expected a macro definition"));
139 }
140 }
143
144 let arg = ctx.parser.gullet.consume_arg(None)?;
145 let mut tokens = arg.tokens;
146
147 if insert_brace {
148 let brace_tok = ratex_lexer::token::Token::new("{", 0, 0);
149 tokens.push(brace_tok);
150 }
151
152 if ctx.func_name == "\\edef" || ctx.func_name == "\\xdef" {
153 tokens.reverse();
154 tokens = ctx.parser.gullet.expand_tokens(tokens)?;
155 }
156
157 let is_global_def = ctx.func_name == "\\gdef" || ctx.func_name == "\\xdef";
158 let def = MacroDefinition::Tokens { tokens, num_args };
159 if is_global_def {
160 ctx.parser.gullet.set_macro_global(name, def);
161 } else {
162 ctx.parser.gullet.set_macro(name, def);
163 }
164
165 Ok(ParseNode::Internal {
166 mode: ctx.parser.mode,
167 loc: None,
168 })
169}
170
171fn handle_let(
172 ctx: &mut FunctionContext,
173 _args: Vec<ParseNode>,
174 _opt_args: Vec<Option<ParseNode>>,
175) -> ParseResult<ParseNode> {
176 let name_tok = ctx.parser.gullet.pop_token();
177 let name = name_tok.text.clone();
178 check_control_sequence(&name)?;
179
180 ctx.parser.gullet.consume_spaces();
181
182 let mut tok = ctx.parser.gullet.pop_token();
184 if tok.text == "=" {
185 tok = ctx.parser.gullet.pop_token();
186 if tok.text == " " {
187 tok = ctx.parser.gullet.pop_token();
188 }
189 }
190
191 let is_global = ctx.func_name == "\\\\globallet";
192 let_command(ctx, &name, tok, is_global);
193
194 Ok(ParseNode::Internal {
195 mode: ctx.parser.mode,
196 loc: None,
197 })
198}
199
200fn handle_futurelet(
201 ctx: &mut FunctionContext,
202 _args: Vec<ParseNode>,
203 _opt_args: Vec<Option<ParseNode>>,
204) -> ParseResult<ParseNode> {
205 let name_tok = ctx.parser.gullet.pop_token();
206 let name = name_tok.text.clone();
207 check_control_sequence(&name)?;
208
209 let middle = ctx.parser.gullet.pop_token();
210 let tok = ctx.parser.gullet.pop_token();
211
212 let is_global = ctx.func_name == "\\\\globalfuture";
213 let_command(ctx, &name, tok.clone(), is_global);
214
215 ctx.parser.gullet.push_token(tok);
216 ctx.parser.gullet.push_token(middle);
217
218 Ok(ParseNode::Internal {
219 mode: ctx.parser.mode,
220 loc: None,
221 })
222}
223
224fn let_command(ctx: &mut FunctionContext, name: &str, mut tok: ratex_lexer::token::Token, global: bool) {
225 let macro_def = ctx.parser.gullet.get_macro(&tok.text).cloned();
226 let def = match macro_def {
227 Some(d) => d,
228 None => {
229 tok.noexpand = true;
230 let unexpandable = !ctx.parser.gullet.is_expandable(&tok.text);
231 let _ = unexpandable;
232 MacroDefinition::Tokens {
233 tokens: vec![tok],
234 num_args: 0,
235 }
236 }
237 };
238
239 if global {
240 ctx.parser.gullet.set_macro_global(name.to_string(), def);
241 } else {
242 ctx.parser.gullet.set_macro(name.to_string(), def);
243 }
244}