use std::collections::HashMap;
use ratex_lexer::token::{SourceLocation, Token};
use ratex_lexer::Lexer;
use crate::error::{ParseError, ParseResult};
use crate::functions::FUNCTIONS;
use crate::parse_node::Mode;
pub static IMPLICIT_COMMANDS: &[&str] = &["^", "_", "\\limits", "\\nolimits"];
pub type FnMacroHandler = fn(&mut MacroExpander) -> ParseResult<Vec<Token>>;
#[derive(Clone)]
pub enum MacroDefinition {
Text(String),
Tokens {
tokens: Vec<Token>,
num_args: usize,
},
Function(FnMacroHandler),
}
impl std::fmt::Debug for MacroDefinition {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Text(s) => write!(f, "Text({:?})", s),
Self::Tokens { tokens, num_args } => {
write!(f, "Tokens {{ {} tokens, {} args }}", tokens.len(), num_args)
}
Self::Function(_) => write!(f, "Function(...)"),
}
}
}
struct MacroExpansion {
tokens: Vec<Token>,
num_args: usize,
unexpandable: bool,
}
pub struct MacroExpander<'a> {
pub lexer: Lexer<'a>,
pub mode: Mode,
stack: Vec<Token>,
macros: MacroNamespace,
expansion_count: usize,
max_expand: usize,
}
struct MacroNamespace {
current: HashMap<String, MacroDefinition>,
group_stack: Vec<HashMap<String, Option<MacroDefinition>>>,
}
impl MacroNamespace {
fn new() -> Self {
Self {
current: HashMap::new(),
group_stack: Vec::new(),
}
}
fn get(&self, name: &str) -> Option<&MacroDefinition> {
self.current.get(name)
}
fn set(&mut self, name: String, def: MacroDefinition) {
if let Some(undo) = self.group_stack.last_mut() {
undo.entry(name.clone()).or_insert_with(|| self.current.get(&name).cloned());
}
self.current.insert(name, def);
}
fn set_global(&mut self, name: String, def: MacroDefinition) {
self.current.insert(name, def);
}
fn has(&self, name: &str) -> bool {
self.current.contains_key(name)
}
fn begin_group(&mut self) {
self.group_stack.push(HashMap::new());
}
fn end_group(&mut self) {
if let Some(undo) = self.group_stack.pop() {
for (name, old_val) in undo {
match old_val {
Some(def) => { self.current.insert(name, def); }
None => { self.current.remove(&name); }
}
}
}
}
fn end_groups(&mut self) {
while !self.group_stack.is_empty() {
self.end_group();
}
}
}
fn lex_string_to_stack_tokens(text: &str) -> Vec<Token> {
let mut body_lexer = Lexer::new(text);
let mut tokens = Vec::new();
loop {
let tok = body_lexer.lex();
if tok.is_eof() {
break;
}
tokens.push(tok);
}
tokens.reverse();
tokens
}
impl<'a> MacroExpander<'a> {
pub fn new(input: &'a str, mode: Mode) -> Self {
let mut me = Self {
lexer: Lexer::new(input),
mode,
stack: Vec::new(),
macros: MacroNamespace::new(),
expansion_count: 0,
max_expand: 1000,
};
me.load_builtins();
me
}
fn load_builtins(&mut self) {
let builtins: &[(&str, &str)] = &[
("\\bgroup", "{"),
("\\egroup", "}"),
("\\lq", "`"),
("\\rq", "'"),
("\\aa", "\\r a"),
("\\AA", "\\r A"),
("~", "\\nobreakspace"),
("\\hphantom", "\\smash{\\phantom{#1}}"),
("\\not", "\\html@mathml{\\mathrel{\\mathrlap\\@not}\\nobreak}{\\char\"338}"),
("\\neq", "\\html@mathml{\\mathrel{\\not=}}{\\mathrel{\\char`≠}}"),
("\\ne", "\\neq"),
("\u{2260}", "\\neq"),
("\\notin", "\\html@mathml{\\mathrel{{\\in}\\mathllap{/\\mskip1mu}}}{\\mathrel{\\char`∉}}"),
("\u{2209}", "\\notin"),
("\\notni", "\\html@mathml{\\not\\ni}{\\mathrel{\\char`\u{220C}}}"),
("\u{220C}", "\\notni"),
("\\iff", "\\DOTSB\\;\\Longleftrightarrow\\;"),
("\\implies", "\\DOTSB\\;\\Longrightarrow\\;"),
("\\impliedby", "\\DOTSB\\;\\Longleftarrow\\;"),
("\\varGamma", "\\mathit{\\Gamma}"),
("\\varDelta", "\\mathit{\\Delta}"),
("\\varTheta", "\\mathit{\\Theta}"),
("\\varLambda", "\\mathit{\\Lambda}"),
("\\varXi", "\\mathit{\\Xi}"),
("\\varPi", "\\mathit{\\Pi}"),
("\\varSigma", "\\mathit{\\Sigma}"),
("\\varUpsilon", "\\mathit{\\Upsilon}"),
("\\varPhi", "\\mathit{\\Phi}"),
("\\varPsi", "\\mathit{\\Psi}"),
("\\varOmega", "\\mathit{\\Omega}"),
("\\,", "\\TextOrMath{\\kern{.1667em}}{\\mskip{3mu}}"),
("\\thinspace", "\\,"),
("\\>", "\\mskip{4mu}"),
("\\:", "\\TextOrMath{\\kern{.2222em}}{\\mskip{4mu}}"),
("\\medspace", "\\:"),
("\\;", "\\TextOrMath{\\kern{.2777em}}{\\mskip{5mu}}"),
("\\thickspace", "\\;"),
("\\!", "\\TextOrMath{\\kern{-.1667em}}{\\mskip{-3mu}}"),
("\\negthinspace", "\\!"),
("\\negmedspace", "\\TextOrMath{\\kern{-.2222em}}{\\mskip{-4mu}}"),
("\\negthickspace", "\\TextOrMath{\\kern{-.2777em}}{\\mskip{-5mu}}"),
("\\enspace", "\\kern.5em "),
("\\enskip", "\\hskip.5em\\relax"),
("\\quad", "\\hskip1em\\relax"),
("\\qquad", "\\hskip2em\\relax"),
("\\newline", "\\\\\\relax"),
("\\@hspace", "\\hskip #1\\relax"),
("\\@hspacer", "\\rule{0pt}{0pt}\\hskip #1\\relax"),
("\\llap", "\\mathllap{\\textrm{#1}}"),
("\\rlap", "\\mathrlap{\\textrm{#1}}"),
("\\clap", "\\mathclap{\\textrm{#1}}"),
("\\TeX", "\\textrm{\\html@mathml{T\\kern-.1667em\\raisebox{-.5ex}{E}\\kern-.125emX}{TeX}}"),
("\\LaTeX", "\\textrm{\\html@mathml{L\\kern-.36em\\raisebox{0.21em}{\\scriptstyle A}\\kern-.15em\\TeX}{LaTeX}}"),
("\\KaTeX", "\\textrm{\\html@mathml{K\\kern-.17em\\raisebox{0.21em}{\\scriptstyle A}\\kern-.15em\\TeX}{KaTeX}}"),
("\\imath", "\\html@mathml{\\@imath}{\u{0131}}"),
("\\jmath", "\\html@mathml{\\@jmath}{\u{0237}}"),
("\\minuso", "\\mathbin{\\html@mathml{{\\mathrlap{\\mathchoice{\\kern{0.145em}}{\\kern{0.145em}}{\\kern{0.1015em}}{\\kern{0.0725em}}\\circ}{-}}}{\\char`\u{29B5}}}"),
("\\clap", "\\mathclap{\\textrm{#1}}"),
("\\mathstrut", "\\vphantom{(}"),
("\\underbar", "\\underline{\\text{#1}}"),
("\\Bbbk", "\\Bbb{k}"),
("\\substack", "\\begin{subarray}{c}#1\\end{subarray}"),
("\\boxed", "\\fbox{$\\displaystyle{#1}$}"),
("\\colon", "\\nobreak\\mskip2mu\\mathpunct{}\\mathchoice{\\mkern-3mu}{\\mkern-3mu}{}{}{:}\\mskip6mu\\relax"),
("\\dots", "\\cdots"),
("\\cdots", "\\@cdots"),
("\\dotsb", "\\cdots"),
("\\dotsm", "\\cdots"),
("\\dotsi", "\\!\\cdots"),
("\\dotsx", "\\ldots\\,"),
("\\dotsc", "\\ldots"), ("\\dotso", "\\ldots"), ("\\DOTSI", "\\relax"),
("\\DOTSB", "\\relax"),
("\\DOTSX", "\\relax"),
("\\gvertneqq", "\\@gvertneqq"),
("\\lvertneqq", "\\@lvertneqq"),
("\\ngeqq", "\\@ngeqq"),
("\\ngeqslant", "\\@ngeqslant"),
("\\nleqq", "\\@nleqq"),
("\\nleqslant", "\\@nleqslant"),
("\\nshortmid", "\\@nshortmid"),
("\\nshortparallel", "\\@nshortparallel"),
("\\nsubseteqq", "\\@nsubseteqq"),
("\\nsupseteqq", "\\@nsupseteqq"),
("\\ulcorner", "\\@ulcorner"),
("\\urcorner", "\\@urcorner"),
("\\llcorner", "\\@llcorner"),
("\\lrcorner", "\\@lrcorner"),
("\\varsubsetneq", "\\@varsubsetneq"),
("\\varsubsetneqq", "\\@varsubsetneqq"),
("\\varsupsetneq", "\\@varsupsetneq"),
("\\varsupsetneqq", "\\@varsupsetneqq"),
("\\lBrace", "\\mathopen{\\{\\mkern-3.2mu[}"),
("\\rBrace", "\\mathclose{]\\mkern-3.2mu\\}}"),
("\\llbracket", "\\mathopen{[\\mkern-3.2mu[}"),
("\\rrbracket", "\\mathclose{]\\mkern-3.2mu]}"),
("\\copyright", "\\textcircled{c}"),
("\\textregistered", "\\textcircled{\\scriptsize R}"),
("\\dddot", "{\\overset{\\raisebox{-0.1ex}{\\normalsize ...}}{#1}}"),
("\\ddddot", "{\\overset{\\raisebox{-0.1ex}{\\normalsize ....}}{#1}}"),
("\\vdots", "{\\varvdots\\rule{0pt}{15pt}}"),
("\u{22ee}", "\\vdots"),
("\\bmod", "\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}\\mathbin{\\rm mod}\\mathchoice{\\mskip1mu}{\\mskip1mu}{\\mskip5mu}{\\mskip5mu}"),
("\\pod", "\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern8mu}{\\mkern8mu}{\\mkern8mu}(#1)"),
("\\pmod", "\\pod{{\\rm mod}\\mkern6mu#1}"),
("\\mod", "\\allowbreak\\mathchoice{\\mkern18mu}{\\mkern12mu}{\\mkern12mu}{\\mkern12mu}{\\rm mod}\\,\\,#1"),
("\\limsup", "\\DOTSB\\operatorname*{lim\\,sup}"),
("\\liminf", "\\DOTSB\\operatorname*{lim\\,inf}"),
("\\injlim", "\\DOTSB\\operatorname*{inj\\,lim}"),
("\\projlim", "\\DOTSB\\operatorname*{proj\\,lim}"),
("\\varlimsup", "\\DOTSB\\operatorname*{\\overline{\\mathrm{lim}}}"),
("\\varliminf", "\\DOTSB\\operatorname*{\\underline{\\mathrm{lim}}}"),
("\\varinjlim", "\\DOTSB\\operatorname*{\\underrightarrow{\\mathrm{lim}}}"),
("\\varprojlim", "\\DOTSB\\operatorname*{\\underleftarrow{\\mathrm{lim}}}"),
("\\argmin", "\\DOTSB\\operatorname*{arg\\,min}"),
("\\argmax", "\\DOTSB\\operatorname*{arg\\,max}"),
("\\plim", "\\DOTSB\\mathop{\\operatorname{plim}}\\limits"),
("\\ordinarycolon", ":"),
("\\vcentcolon", "\\mathrel{\\mathop\\ordinarycolon}"),
("\\dblcolon", "\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-.9mu}\\vcentcolon}}{\\mathop{\\char\"2237}}"),
("\\coloneqq", "\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char\"2254}}"),
("\\Coloneqq", "\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}=}}{\\mathop{\\char\"2237\\char\"3d}}"),
("\\coloneq", "\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char\"3a\\char\"2212}}"),
("\\Coloneq", "\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\mathrel{-}}}{\\mathop{\\char\"2237\\char\"2212}}"),
("\\eqqcolon", "\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char\"2255}}"),
("\\Eqqcolon", "\\html@mathml{\\mathrel{=\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char\"3d\\char\"2237}}"),
("\\eqcolon", "\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\vcentcolon}}{\\mathop{\\char\"2239}}"),
("\\Eqcolon", "\\html@mathml{\\mathrel{\\mathrel{-}\\mathrel{\\mkern-1.2mu}\\dblcolon}}{\\mathop{\\char\"2212\\char\"2237}}"),
("\\colonapprox", "\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char\"3a\\char\"2248}}"),
("\\Colonapprox", "\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\approx}}{\\mathop{\\char\"2237\\char\"2248}}"),
("\\colonsim", "\\html@mathml{\\mathrel{\\vcentcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char\"3a\\char\"223c}}"),
("\\Colonsim", "\\html@mathml{\\mathrel{\\dblcolon\\mathrel{\\mkern-1.2mu}\\sim}}{\\mathop{\\char\"2237\\char\"223c}}"),
("\\ratio", "\\vcentcolon"),
("\\coloncolon", "\\dblcolon"),
("\\colonequals", "\\coloneqq"),
("\\coloncolonequals", "\\Coloneqq"),
("\\equalscolon", "\\eqqcolon"),
("\\equalscoloncolon", "\\Eqqcolon"),
("\\colonminus", "\\coloneq"),
("\\coloncolonminus", "\\Coloneq"),
("\\minuscolon", "\\eqcolon"),
("\\minuscoloncolon", "\\Eqcolon"),
("\\coloncolonapprox", "\\Colonapprox"),
("\\coloncolonsim", "\\Colonsim"),
("\\simcolon", "\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),
("\\simcoloncolon", "\\mathrel{\\sim\\mathrel{\\mkern-1.2mu}\\dblcolon}"),
("\\approxcolon", "\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\vcentcolon}"),
("\\approxcoloncolon", "\\mathrel{\\approx\\mathrel{\\mkern-1.2mu}\\dblcolon}"),
("\\bra", "\\mathinner{\\langle{#1}|}"),
("\\ket", "\\mathinner{|{#1}\\rangle}"),
("\\braket", "\\mathinner{\\langle{#1}\\rangle}"),
("\\Braket", "\\bra@ket{\\left\\langle}{\\,\\middle\\vert\\,}{\\,\\middle\\vert\\,}{\\right\\rangle}"),
("\\Bra", "\\left\\langle#1\\right|"),
("\\Ket", "\\left|#1\\right\\rangle"),
("\\darr", "\\downarrow"),
("\\dArr", "\\Downarrow"),
("\\Darr", "\\Downarrow"),
("\\lang", "\\langle"),
("\\rang", "\\rangle"),
("\\uarr", "\\uparrow"),
("\\uArr", "\\Uparrow"),
("\\Uarr", "\\Uparrow"),
("\\N", "\\mathbb{N}"),
("\\R", "\\mathbb{R}"),
("\\Z", "\\mathbb{Z}"),
("\\alef", "\\aleph"),
("\\alefsym", "\\aleph"),
("\\Alpha", "\\mathrm{A}"),
("\\Beta", "\\mathrm{B}"),
("\\bull", "\\bullet"),
("\\Chi", "\\mathrm{X}"),
("\\clubs", "\\clubsuit"),
("\\cnums", "\\mathbb{C}"),
("\\Complex", "\\mathbb{C}"),
("\\Dagger", "\\ddagger"),
("\\diamonds", "\\diamondsuit"),
("\\empty", "\\emptyset"),
("\\Epsilon", "\\mathrm{E}"),
("\\Eta", "\\mathrm{H}"),
("\\exist", "\\exists"),
("\\harr", "\\leftrightarrow"),
("\\hArr", "\\Leftrightarrow"),
("\\Harr", "\\Leftrightarrow"),
("\\hearts", "\\heartsuit"),
("\\image", "\\Im"),
("\\infin", "\\infty"),
("\\Iota", "\\mathrm{I}"),
("\\isin", "\\in"),
("\\Kappa", "\\mathrm{K}"),
("\\larr", "\\leftarrow"),
("\\lArr", "\\Leftarrow"),
("\\Larr", "\\Leftarrow"),
("\\lrarr", "\\leftrightarrow"),
("\\lrArr", "\\Leftrightarrow"),
("\\Lrarr", "\\Leftrightarrow"),
("\\Mu", "\\mathrm{M}"),
("\\natnums", "\\mathbb{N}"),
("\\Nu", "\\mathrm{N}"),
("\\Omicron", "\\mathrm{O}"),
("\\plusmn", "\\pm"),
("\\rarr", "\\rightarrow"),
("\\rArr", "\\Rightarrow"),
("\\Rarr", "\\Rightarrow"),
("\\real", "\\Re"),
("\\reals", "\\mathbb{R}"),
("\\Reals", "\\mathbb{R}"),
("\\Rho", "\\mathrm{P}"),
("\\sdot", "\\cdot"),
("\\sect", "\\S"),
("\\spades", "\\spadesuit"),
("\\sub", "\\subset"),
("\\sube", "\\subseteq"),
("\\supe", "\\supseteq"),
("\\Tau", "\\mathrm{T}"),
("\\thetasym", "\\vartheta"),
("\\weierp", "\\wp"),
("\\Zeta", "\\mathrm{Z}"),
("\\blue", "\\textcolor{##6495ed}{#1}"),
("\\orange", "\\textcolor{##ffa500}{#1}"),
("\\pink", "\\textcolor{##ff00af}{#1}"),
("\\red", "\\textcolor{##df0030}{#1}"),
("\\green", "\\textcolor{##28ae7b}{#1}"),
("\\gray", "\\textcolor{gray}{#1}"),
("\\purple", "\\textcolor{##9d38bd}{#1}"),
("\u{212C}", "\\mathscr{B}"),
("\u{2130}", "\\mathscr{E}"),
("\u{2131}", "\\mathscr{F}"),
("\u{210B}", "\\mathscr{H}"),
("\u{2110}", "\\mathscr{I}"),
("\u{2112}", "\\mathscr{L}"),
("\u{2133}", "\\mathscr{M}"),
("\u{211B}", "\\mathscr{R}"),
("\u{212D}", "\\mathfrak{C}"),
("\u{210C}", "\\mathfrak{H}"),
("\u{2128}", "\\mathfrak{Z}"),
("\\notni", "\\html@mathml{\\not\\ni}{\\mathrel{\\char`\u{220C}}}"),
("\\angln", "{\\angl n}"),
("\\set", "\\bra@set{\\{\\,}{\\mid}{}{\\,\\}}"),
("\\Set", "\\bra@set{\\left\\{\\:}{\\;\\middle\\vert\\;}{\\;\\middle\\Vert\\;}{\\:\\right\\}}"),
("\\nonumber", "\\relax"),
("\\notag", "\\relax"),
(
"\\tripledash",
"{\\vphantom{-}\\raisebox{2.56mu}{$\\mkern2mu\\tiny\\text{-}\\mkern1mu\\text{-}\\mkern1mu\\text{-}\\mkern2mu$}}",
),
];
for &(name, expansion) in builtins {
self.macros.set(
name.to_string(),
MacroDefinition::Text(expansion.to_string()),
);
}
self.load_function_macros();
}
fn load_function_macros(&mut self) {
self.macros.set(
"\\noexpand".to_string(),
MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
let mut tok = me.pop_token();
if me.is_expandable(&tok.text) {
tok.noexpand = true;
tok.treat_as_relax = true;
}
Ok(vec![tok])
}),
);
self.macros.set(
"\\@firstoftwo".to_string(),
MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
let args = me.consume_args(2)?;
Ok(args.into_iter().next().unwrap())
}),
);
self.macros.set(
"\\@secondoftwo".to_string(),
MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
let args = me.consume_args(2)?;
Ok(args.into_iter().nth(1).unwrap())
}),
);
self.macros.set(
"\\@ifnextchar".to_string(),
MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
let args = me.consume_args(3)?;
me.consume_spaces();
let next = me.future().text.clone();
let char_arg = &args[0];
let char_text = char_arg.first().map_or("", |t| t.text.as_str());
if next == char_text {
Ok(args[1].clone())
} else {
Ok(args[2].clone())
}
}),
);
self.macros.set(
"\\@ifstar".to_string(),
MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
let args = me.consume_args(2)?;
let next = me.future().text.clone();
if next == "*" {
me.pop_token();
Ok(args[0].clone())
} else {
Ok(args[1].clone())
}
}),
);
self.macros.set(
"\\TextOrMath".to_string(),
MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
let args = me.consume_args(2)?;
if me.mode == Mode::Text {
Ok(args[0].clone())
} else {
Ok(args[1].clone())
}
}),
);
self.macros.set(
"\\newcommand".to_string(),
MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
handle_newcommand(me, false, true)
}),
);
self.macros.set(
"\\renewcommand".to_string(),
MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
handle_newcommand(me, true, false)
}),
);
self.macros.set(
"\\providecommand".to_string(),
MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
handle_newcommand(me, true, true)
}),
);
self.macros.set(
"\\char".to_string(),
MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
let mut tok = me.pop_token();
let mut number: i64;
let base: Option<u32>;
if tok.text == "'" {
base = Some(8);
tok = me.pop_token();
} else if tok.text == "\"" {
base = Some(16);
tok = me.pop_token();
} else if tok.text == "`" {
tok = me.pop_token();
if tok.text.starts_with('\\') {
number = tok.text.chars().nth(1).map_or(0, |c| c as i64);
} else {
number = tok.text.chars().next().map_or(0, |c| c as i64);
}
let s = number.to_string();
let loc = tok.loc.clone();
let mut result = vec![Token::new("}", loc.start, loc.end)];
for ch in s.chars().rev() {
result.push(Token::new(ch.to_string(), loc.start, loc.end));
}
result.push(Token::new("{", loc.start, loc.end));
result.push(Token::new("\\@char", loc.start, loc.end));
return Ok(result);
} else {
base = Some(10);
}
if let Some(b) = base {
number = i64::from_str_radix(&tok.text, b).unwrap_or(0);
loop {
let next = me.future().text.clone();
if let Ok(d) = i64::from_str_radix(&next, b) {
me.pop_token();
number = number * (b as i64) + d;
} else {
break;
}
}
} else {
number = 0;
}
let s = number.to_string();
let loc = tok.loc.clone();
let mut result = vec![Token::new("}", loc.start, loc.end)];
for ch in s.chars().rev() {
result.push(Token::new(ch.to_string(), loc.start, loc.end));
}
result.push(Token::new("{", loc.start, loc.end));
result.push(Token::new("\\@char", loc.start, loc.end));
Ok(result)
}),
);
self.macros.set(
"\\operatorname".to_string(),
MacroDefinition::Text(
"\\@ifstar\\operatornamewithlimits\\operatorname@".to_string(),
),
);
self.macros.set(
"\\message".to_string(),
MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
let _args = me.consume_args(1)?;
Ok(vec![])
}),
);
self.macros.set(
"\\errmessage".to_string(),
MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
let _args = me.consume_args(1)?;
Ok(vec![])
}),
);
for name in &["\\htmlClass", "\\htmlData", "\\htmlId", "\\htmlStyle"] {
let name = (*name).to_string();
self.macros.set(
name.clone(),
MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
let args = me.consume_args(2)?;
let content = args[1].iter().cloned().rev().collect::<Vec<_>>();
Ok(content)
}),
);
}
self.macros.set(
"\\bra@ket".to_string(),
MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
let args = me.consume_args(4)?;
let left = args[0].clone();
let middle = args[1].clone();
let middle_double = args[2].clone();
let right = args[3].clone();
let content = me.consume_args(1)?;
let content = content.into_iter().next().unwrap();
let logical: Vec<Token> = content.into_iter().rev().collect();
let mut new_logical: Vec<Token> = Vec::new();
let mut depth: i32 = 0;
let mut i = 0;
while i < logical.len() {
let t = &logical[i];
if t.text == "{" {
depth += 1;
new_logical.push(t.clone());
} else if t.text == "}" {
depth -= 1;
new_logical.push(t.clone());
} else if depth == 0 && t.text == "|" {
if !middle_double.is_empty()
&& i + 1 < logical.len()
&& logical[i + 1].text == "|"
{
new_logical.extend(middle_double.iter().rev().cloned());
i += 2;
continue;
}
new_logical.extend(middle.iter().rev().cloned());
} else {
new_logical.push(t.clone());
}
i += 1;
}
let content_rev: Vec<Token> = new_logical.into_iter().rev().collect();
let mut to_expand = Vec::new();
to_expand.extend(right);
to_expand.extend(content_rev);
to_expand.extend(left);
me.begin_group();
let expanded = me.expand_tokens(to_expand)?;
me.end_group();
Ok(expanded)
}),
);
self.macros.set(
"\\bra@set".to_string(),
MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
let args = me.consume_args(4)?;
let left = args[0].clone();
let middle = args[1].clone();
let middle_double = args[2].clone();
let right = args[3].clone();
let content = me.consume_args(1)?;
let mut content = content.into_iter().next().unwrap();
let mut depth: i32 = 0;
let mut _first_pipe_idx: Option<usize> = None;
for i in (0..content.len()).rev() {
let t = &content[i];
if t.text == "{" { depth += 1; }
else if t.text == "}" { depth -= 1; }
else if depth == 0 && t.text == "|" {
if !middle_double.is_empty() && i > 0 && content[i - 1].text == "|" {
_first_pipe_idx = Some(i);
content.remove(i);
content.remove(i - 1);
let insert_at = if i >= 2 { i - 1 } else { 0 };
for (j, tok) in middle_double.iter().enumerate() {
content.insert(insert_at + j, tok.clone());
}
break;
}
_first_pipe_idx = Some(i);
content.remove(i);
for (j, tok) in middle.iter().enumerate() {
content.insert(i + j, tok.clone());
}
break;
}
}
let mut to_expand = Vec::new();
to_expand.extend(right);
to_expand.extend(content);
to_expand.extend(left);
me.begin_group();
let expanded = me.expand_tokens(to_expand)?;
me.end_group();
Ok(expanded)
}),
);
self.macros.set(
"\\ce".to_string(),
MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
let args = me.consume_args(1)?;
let s = crate::mhchem::mhchem_arg_tokens_to_string(&args[0]);
let tex = crate::mhchem::chem_parse_str(&s, "ce")
.map_err(|e| ParseError::msg(format!("\\ce: {e}")))?;
Ok(lex_string_to_stack_tokens(&tex))
}),
);
self.macros.set(
"\\pu".to_string(),
MacroDefinition::Function(|me: &mut MacroExpander| -> ParseResult<Vec<Token>> {
let args = me.consume_args(1)?;
let s = crate::mhchem::mhchem_arg_tokens_to_string(&args[0]);
let tex = crate::mhchem::chem_parse_str(&s, "pu")
.map_err(|e| ParseError::msg(format!("\\pu: {e}")))?;
Ok(lex_string_to_stack_tokens(&tex))
}),
);
}
pub fn set_macro(&mut self, name: String, def: MacroDefinition) {
self.macros.set(name, def);
}
pub fn set_macro_global(&mut self, name: String, def: MacroDefinition) {
self.macros.set_global(name, def);
}
pub fn set_text_macro(&mut self, name: &str, text: &str) {
self.macros.set(
name.to_string(),
MacroDefinition::Text(text.to_string()),
);
}
pub fn get_macro(&self, name: &str) -> Option<&MacroDefinition> {
self.macros.get(name)
}
pub fn expand_tokens(&mut self, tokens: Vec<Token>) -> ParseResult<Vec<Token>> {
let saved_stack = std::mem::take(&mut self.stack);
self.stack = tokens;
let mut result = Vec::new();
loop {
if self.stack.is_empty() {
break;
}
let expanded = self.expand_once(false)?;
if !expanded {
if let Some(tok) = self.stack.pop() {
if tok.is_eof() {
break;
}
result.push(tok);
}
}
}
self.stack = saved_stack;
result.reverse();
Ok(result)
}
pub fn switch_mode(&mut self, new_mode: Mode) {
self.mode = new_mode;
}
pub fn begin_group(&mut self) {
self.macros.begin_group();
}
pub fn end_group(&mut self) {
self.macros.end_group();
}
pub fn end_groups(&mut self) {
self.macros.end_groups();
}
pub fn future(&mut self) -> &Token {
if self.stack.is_empty() {
let tok = self.lexer.lex();
self.stack.push(tok);
}
self.stack.last().unwrap()
}
pub fn pop_token(&mut self) -> Token {
self.future();
self.stack.pop().unwrap()
}
pub fn set_top_text(&mut self, text: String) {
self.future();
if let Some(tok) = self.stack.last_mut() {
tok.text = text;
}
}
pub fn push_token(&mut self, token: Token) {
self.stack.push(token);
}
pub fn push_tokens(&mut self, tokens: Vec<Token>) {
self.stack.extend(tokens);
}
pub fn consume_spaces(&mut self) {
loop {
let is_space = self.future().text == " ";
if is_space {
self.stack.pop();
} else {
break;
}
}
}
fn expand_once(&mut self, expandable_only: bool) -> ParseResult<bool> {
let top_token = self.pop_token();
let name = &top_token.text;
if top_token.noexpand {
self.push_token(top_token);
return Ok(false);
}
if let Some(MacroDefinition::Function(handler)) = self.macros.get(name).cloned() {
self.count_expansion(1)?;
let tokens = handler(self)?;
self.stack.extend(tokens);
return Ok(true);
}
let expansion = self.get_expansion(name);
match expansion {
None => {
if expandable_only && name.starts_with('\\') && !self.is_defined(name) {
return Err(ParseError::new(
format!("Undefined control sequence: {}", name),
Some(&top_token),
));
}
self.push_token(top_token);
Ok(false)
}
Some(exp) if expandable_only && exp.unexpandable => {
self.push_token(top_token);
Ok(false)
}
Some(exp) => {
self.count_expansion(1)?;
let mut tokens = exp.tokens;
if exp.num_args > 0 {
let args = self.consume_args(exp.num_args)?;
tokens = self.substitute_args(tokens, &args);
}
self.stack.extend(tokens);
Ok(true)
}
}
}
fn substitute_args(&self, mut tokens: Vec<Token>, args: &[Vec<Token>]) -> Vec<Token> {
let mut i = tokens.len();
while i > 0 {
i -= 1;
if tokens[i].text == "#" && i > 0 {
let next = &tokens[i - 1];
if next.text == "#" {
tokens.remove(i);
i -= 1;
} else if let Ok(n) = next.text.parse::<usize>() {
if n >= 1 && n <= args.len() {
tokens.remove(i);
tokens.remove(i - 1);
let arg_tokens = &args[n - 1];
for (j, t) in arg_tokens.iter().enumerate() {
tokens.insert(i - 1 + j, t.clone());
}
i = i.saturating_sub(1);
}
}
}
}
tokens
}
fn get_expansion(&self, name: &str) -> Option<MacroExpansion> {
let def = self.macros.get(name)?;
if name.len() == 1 {
let ch = name.chars().next().unwrap();
let catcode = self.lexer_catcode(ch);
if catcode != 0 && catcode != 13 {
return None;
}
}
match def {
MacroDefinition::Text(text) => {
let mut num_args = 0;
let stripped = text.replace("##", "");
while stripped.contains(&format!("#{}", num_args + 1)) {
num_args += 1;
}
let mut body_lexer = Lexer::new(text);
let mut tokens = Vec::new();
loop {
let tok = body_lexer.lex();
if tok.is_eof() {
break;
}
tokens.push(tok);
}
tokens.reverse();
Some(MacroExpansion {
tokens,
num_args,
unexpandable: false,
})
}
MacroDefinition::Tokens { tokens, num_args } => Some(MacroExpansion {
tokens: tokens.clone(),
num_args: *num_args,
unexpandable: false,
}),
MacroDefinition::Function(_) => {
Some(MacroExpansion {
tokens: vec![],
num_args: 0,
unexpandable: false,
})
}
}
}
fn lexer_catcode(&self, ch: char) -> u8 {
self.lexer.get_catcode(ch)
}
fn count_expansion(&mut self, amount: usize) -> ParseResult<()> {
self.expansion_count += amount;
if self.expansion_count > self.max_expand {
Err(ParseError::msg(
"Too many expansions: infinite loop or need to increase maxExpand setting",
))
} else {
Ok(())
}
}
pub fn expand_next_token(&mut self) -> ParseResult<Token> {
loop {
let expanded = self.expand_once(false)?;
if !expanded {
let mut token = self.stack.pop().unwrap();
if token.treat_as_relax {
token.text = "\\relax".to_string();
}
return Ok(token);
}
}
}
pub fn consume_arg(&mut self, delims: Option<&[&str]>) -> ParseResult<ConsumedArg> {
let is_delimited = delims.is_some_and(|d| !d.is_empty());
if !is_delimited {
self.consume_spaces();
}
let start = self.future().clone();
let mut tokens = Vec::new();
let mut depth: i32 = 0;
let mut end_tok;
loop {
let tok = self.pop_token();
end_tok = tok.clone();
tokens.push(tok.clone());
if tok.text == "{" {
depth += 1;
} else if tok.text == "}" {
depth -= 1;
if depth == -1 {
return Err(ParseError::new("Extra }", Some(&tok)));
}
} else if tok.is_eof() {
return Err(ParseError::new(
"Unexpected end of input in a macro argument",
Some(&tok),
));
}
if depth == 0 && !is_delimited {
break;
}
if let Some(delims) = delims {
if is_delimited && depth == 0 {
if let Some(last) = delims.last() {
if tok.text == *last {
tokens.pop();
break;
}
}
}
}
}
if start.text == "{" && tokens.last().is_some_and(|t| t.text == "}") {
tokens.pop();
tokens.remove(0);
}
tokens.reverse();
Ok(ConsumedArg {
tokens,
start,
end: end_tok,
})
}
fn consume_args(&mut self, num_args: usize) -> ParseResult<Vec<Vec<Token>>> {
let mut args = Vec::with_capacity(num_args);
for _ in 0..num_args {
let arg = self.consume_arg(None)?;
args.push(arg.tokens);
}
Ok(args)
}
pub fn scan_argument(&mut self, is_optional: bool) -> ParseResult<Option<Token>> {
if is_optional {
self.consume_spaces();
if self.future().text != "[" {
return Ok(None);
}
let start = self.pop_token();
let arg = self.consume_arg(Some(&["]"]))?;
let end = &arg.end;
let end_loc = end.loc.clone();
self.push_token(Token::new("EOF", end_loc.start, end_loc.end));
self.push_tokens(arg.tokens);
let result = Token {
text: String::new(),
loc: SourceLocation::range(&start.loc, &end_loc),
noexpand: false,
treat_as_relax: false,
};
Ok(Some(result))
} else {
let arg = self.consume_arg(None)?;
let end_loc = arg.end.loc.clone();
self.push_token(Token::new("EOF", end_loc.start, end_loc.end));
self.push_tokens(arg.tokens);
let result = Token {
text: String::new(),
loc: SourceLocation::range(&arg.start.loc, &end_loc),
noexpand: false,
treat_as_relax: false,
};
Ok(Some(result))
}
}
pub fn is_defined(&self, name: &str) -> bool {
self.macros.has(name)
|| FUNCTIONS.contains_key(name)
|| is_known_symbol(name)
|| IMPLICIT_COMMANDS.contains(&name)
}
pub fn is_expandable(&self, name: &str) -> bool {
if let Some(_def) = self.macros.get(name) {
return true;
}
if let Some(func) = FUNCTIONS.get(name) {
return !func.primitive;
}
false
}
}
pub struct ConsumedArg {
pub tokens: Vec<Token>,
pub start: Token,
pub end: Token,
}
fn handle_newcommand(
me: &mut MacroExpander,
exists_ok: bool,
nonexists_ok: bool,
) -> ParseResult<Vec<Token>> {
let name_arg = me.consume_arg(None)?;
let name = name_arg.tokens.last().map_or_else(String::new, |t| t.text.clone());
let exists = me.is_defined(&name);
if exists && !exists_ok {
return Err(ParseError::msg(format!(
"\\newcommand{{{}}} attempting to redefine {}; use \\renewcommand",
name, name
)));
}
if !exists && !nonexists_ok {
return Err(ParseError::msg(format!(
"\\renewcommand{{{}}} when command {} does not yet exist; use \\newcommand",
name, name
)));
}
me.consume_spaces();
let mut num_args = 0usize;
if me.future().text == "[" {
me.pop_token();
let narg_tok = me.pop_token();
num_args = narg_tok.text.parse().unwrap_or(0);
let close = me.pop_token();
if close.text != "]" {
return Err(ParseError::msg("Expected ] in \\newcommand"));
}
}
let body_arg = me.consume_arg(None)?;
let tokens = body_arg.tokens;
me.set_macro(name, MacroDefinition::Tokens { tokens, num_args });
Ok(vec![])
}
fn is_known_symbol(name: &str) -> bool {
use ratex_font::symbols;
symbols::get_symbol(name, symbols::Mode::Math).is_some()
|| symbols::get_symbol(name, symbols::Mode::Text).is_some()
}