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, 0, None,
38        false,
39        true, true,
40        false, false,
41        handle_prefix,
42    );
43
44    // \def, \gdef, \edef, \xdef
45    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    // \let
57    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    // \futurelet
69    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    // Parse parameter text: read tokens until `{`
119    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        // Delimiter tokens between parameters are consumed but not stored
141        // (simplified: we don't support delimited macros with inter-parameter text)
142    }
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    // Consume optional `=`
183    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}