Skip to main content

ratex_parser/functions/
def.rs

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    // Prefix commands: \global, \long
33    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    // \def, \gdef, \edef, \xdef
49    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    // \let
65    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    // \futurelet
81    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    // Parse parameter text: read tokens until `{`
135    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        // Delimiter tokens between parameters are consumed but not stored
157        // (simplified: we don't support delimited macros with inter-parameter text)
158    }
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    // Consume optional `=`
199    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}