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,
38 0,
39 None,
40 false,
41 true,
42 true,
43 false,
44 false,
45 handle_prefix,
46 );
47
48 define_function_full(
50 map,
51 &["\\def", "\\gdef", "\\edef", "\\xdef"],
52 "internal",
53 0,
54 0,
55 None,
56 false,
57 true,
58 true,
59 false,
60 true,
61 handle_def,
62 );
63
64 define_function_full(
66 map,
67 &["\\let", "\\\\globallet"],
68 "internal",
69 0,
70 0,
71 None,
72 false,
73 true,
74 true,
75 false,
76 true,
77 handle_let,
78 );
79
80 define_function_full(
82 map,
83 &["\\futurelet", "\\\\globalfuture"],
84 "internal",
85 0,
86 0,
87 None,
88 false,
89 true,
90 true,
91 false,
92 true,
93 handle_futurelet,
94 );
95}
96
97fn handle_prefix(
98 ctx: &mut FunctionContext,
99 _args: Vec<ParseNode>,
100 _opt_args: Vec<Option<ParseNode>>,
101) -> ParseResult<ParseNode> {
102 ctx.parser.consume_spaces()?;
103 let mut next_tok = ctx.parser.fetch()?.clone();
104 let token_text = next_tok.text.clone();
105
106 if let Some(global_ver) = global_version(&token_text) {
107 if ctx.func_name == "\\global" || ctx.func_name == "\\\\globallong" {
108 next_tok.text = global_ver.to_string();
109 ctx.parser.gullet.push_token(next_tok);
110 ctx.parser.consume();
111 }
112 let result = ctx.parser.parse_function(None, None)?;
113 match result {
114 Some(node) => Ok(node),
115 None => Err(ParseError::msg("Invalid token after macro prefix")),
116 }
117 } else {
118 Err(ParseError::msg("Invalid token after macro prefix"))
119 }
120}
121
122fn handle_def(
123 ctx: &mut FunctionContext,
124 _args: Vec<ParseNode>,
125 _opt_args: Vec<Option<ParseNode>>,
126) -> ParseResult<ParseNode> {
127 let name_tok = ctx.parser.gullet.pop_token();
128 let name = name_tok.text.clone();
129 check_control_sequence(&name)?;
130
131 let mut num_args = 0usize;
132 let mut insert_brace = false;
133
134 while ctx.parser.gullet.future().text != "{" {
136 let tok = ctx.parser.gullet.pop_token();
137 if tok.text == "#" {
138 if ctx.parser.gullet.future().text == "{" {
139 insert_brace = true;
140 break;
141 }
142 let num_tok = ctx.parser.gullet.pop_token();
143 let n: usize = num_tok.text.parse().map_err(|_| {
144 ParseError::msg(format!("Invalid argument number \"{}\"", num_tok.text))
145 })?;
146 if n != num_args + 1 {
147 return Err(ParseError::msg(format!(
148 "Argument number \"{}\" out of order",
149 n
150 )));
151 }
152 num_args += 1;
153 } else if tok.is_eof() {
154 return Err(ParseError::msg("Expected a macro definition"));
155 }
156 }
159
160 let arg = ctx.parser.gullet.consume_arg(None)?;
161 let mut tokens = arg.tokens;
162
163 if insert_brace {
164 let brace_tok = ratex_lexer::token::Token::new("{", 0, 0);
165 tokens.push(brace_tok);
166 }
167
168 if ctx.func_name == "\\edef" || ctx.func_name == "\\xdef" {
169 tokens.reverse();
170 tokens = ctx.parser.gullet.expand_tokens(tokens)?;
171 }
172
173 let is_global_def = ctx.func_name == "\\gdef" || ctx.func_name == "\\xdef";
174 let def = MacroDefinition::Tokens { tokens, num_args };
175 if is_global_def {
176 ctx.parser.gullet.set_macro_global(name, def);
177 } else {
178 ctx.parser.gullet.set_macro(name, def);
179 }
180
181 Ok(ParseNode::Internal {
182 mode: ctx.parser.mode,
183 loc: None,
184 })
185}
186
187fn handle_let(
188 ctx: &mut FunctionContext,
189 _args: Vec<ParseNode>,
190 _opt_args: Vec<Option<ParseNode>>,
191) -> ParseResult<ParseNode> {
192 let name_tok = ctx.parser.gullet.pop_token();
193 let name = name_tok.text.clone();
194 check_control_sequence(&name)?;
195
196 ctx.parser.gullet.consume_spaces();
197
198 let mut tok = ctx.parser.gullet.pop_token();
200 if tok.text == "=" {
201 tok = ctx.parser.gullet.pop_token();
202 if tok.text == " " {
203 tok = ctx.parser.gullet.pop_token();
204 }
205 }
206
207 let is_global = ctx.func_name == "\\\\globallet";
208 let_command(ctx, &name, tok, is_global);
209
210 Ok(ParseNode::Internal {
211 mode: ctx.parser.mode,
212 loc: None,
213 })
214}
215
216fn handle_futurelet(
217 ctx: &mut FunctionContext,
218 _args: Vec<ParseNode>,
219 _opt_args: Vec<Option<ParseNode>>,
220) -> ParseResult<ParseNode> {
221 let name_tok = ctx.parser.gullet.pop_token();
222 let name = name_tok.text.clone();
223 check_control_sequence(&name)?;
224
225 let middle = ctx.parser.gullet.pop_token();
226 let tok = ctx.parser.gullet.pop_token();
227
228 let is_global = ctx.func_name == "\\\\globalfuture";
229 let_command(ctx, &name, tok.clone(), is_global);
230
231 ctx.parser.gullet.push_token(tok);
232 ctx.parser.gullet.push_token(middle);
233
234 Ok(ParseNode::Internal {
235 mode: ctx.parser.mode,
236 loc: None,
237 })
238}
239
240fn let_command(
241 ctx: &mut FunctionContext,
242 name: &str,
243 mut tok: ratex_lexer::token::Token,
244 global: bool,
245) {
246 let macro_def = ctx.parser.gullet.get_macro(&tok.text).cloned();
247 let def = match macro_def {
248 Some(d) => d,
249 None => {
250 tok.noexpand = true;
251 let unexpandable = !ctx.parser.gullet.is_expandable(&tok.text);
252 let _ = unexpandable;
253 MacroDefinition::Tokens {
254 tokens: vec![tok],
255 num_args: 0,
256 }
257 }
258 };
259
260 if global {
261 ctx.parser.gullet.set_macro_global(name.to_string(), def);
262 } else {
263 ctx.parser.gullet.set_macro(name.to_string(), def);
264 }
265}