1use std::collections::HashMap;
2
3use crate::error::{ParseError, ParseResult};
4use crate::functions::{define_function_full, ArgType, FunctionContext, FunctionSpec};
5use crate::parse_node::ParseNode;
6
7struct DelimInfo {
8 mclass: &'static str,
9 size: u8,
10}
11
12fn delim_info(name: &str) -> Option<DelimInfo> {
13 match name {
14 "\\bigl" => Some(DelimInfo {
15 mclass: "mopen",
16 size: 1,
17 }),
18 "\\Bigl" => Some(DelimInfo {
19 mclass: "mopen",
20 size: 2,
21 }),
22 "\\biggl" => Some(DelimInfo {
23 mclass: "mopen",
24 size: 3,
25 }),
26 "\\Biggl" => Some(DelimInfo {
27 mclass: "mopen",
28 size: 4,
29 }),
30 "\\bigr" => Some(DelimInfo {
31 mclass: "mclose",
32 size: 1,
33 }),
34 "\\Bigr" => Some(DelimInfo {
35 mclass: "mclose",
36 size: 2,
37 }),
38 "\\biggr" => Some(DelimInfo {
39 mclass: "mclose",
40 size: 3,
41 }),
42 "\\Biggr" => Some(DelimInfo {
43 mclass: "mclose",
44 size: 4,
45 }),
46 "\\bigm" => Some(DelimInfo {
47 mclass: "mrel",
48 size: 1,
49 }),
50 "\\Bigm" => Some(DelimInfo {
51 mclass: "mrel",
52 size: 2,
53 }),
54 "\\biggm" => Some(DelimInfo {
55 mclass: "mrel",
56 size: 3,
57 }),
58 "\\Biggm" => Some(DelimInfo {
59 mclass: "mrel",
60 size: 4,
61 }),
62 "\\big" => Some(DelimInfo {
63 mclass: "mord",
64 size: 1,
65 }),
66 "\\Big" => Some(DelimInfo {
67 mclass: "mord",
68 size: 2,
69 }),
70 "\\bigg" => Some(DelimInfo {
71 mclass: "mord",
72 size: 3,
73 }),
74 "\\Bigg" => Some(DelimInfo {
75 mclass: "mord",
76 size: 4,
77 }),
78 _ => None,
79 }
80}
81
82pub fn register(map: &mut HashMap<&'static str, FunctionSpec>) {
83 define_function_full(
84 map,
85 &[
86 "\\bigl", "\\Bigl", "\\biggl", "\\Biggl", "\\bigr", "\\Bigr", "\\biggr", "\\Biggr",
87 "\\bigm", "\\Bigm", "\\biggm", "\\Biggm", "\\big", "\\Big", "\\bigg", "\\Bigg",
88 ],
89 "delimsizing",
90 1,
91 0,
92 Some(vec![ArgType::Primitive]),
93 false,
94 false,
95 true,
96 false,
97 false,
98 handle_delimsizing,
99 );
100}
101
102fn handle_delimsizing(
103 ctx: &mut FunctionContext,
104 args: Vec<ParseNode>,
105 _opt_args: Vec<Option<ParseNode>>,
106) -> ParseResult<ParseNode> {
107 let delim = check_delimiter(&args[0], &ctx.func_name)?;
108 let info = delim_info(&ctx.func_name).unwrap();
109
110 Ok(ParseNode::DelimSizing {
111 mode: ctx.parser.mode,
112 size: info.size,
113 mclass: info.mclass.to_string(),
114 delim,
115 loc: None,
116 })
117}
118
119fn check_delimiter(node: &ParseNode, func_name: &str) -> ParseResult<String> {
120 if let Some(text) = node.symbol_text() {
121 if is_valid_delimiter(text) {
122 return Ok(text.to_string());
123 }
124 return Err(ParseError::msg(format!(
125 "Invalid delimiter '{}' after '{}'",
126 text, func_name
127 )));
128 }
129 Err(ParseError::msg(format!(
130 "Invalid delimiter type '{}' after '{}'",
131 node.type_name(),
132 func_name
133 )))
134}
135
136fn is_valid_delimiter(text: &str) -> bool {
137 matches!(
138 text,
139 "(" | "\\lparen"
140 | ")"
141 | "\\rparen"
142 | "["
143 | "\\lbrack"
144 | "]"
145 | "\\rbrack"
146 | "\\{"
147 | "\\lbrace"
148 | "\\}"
149 | "\\rbrace"
150 | "\\lfloor"
151 | "\\rfloor"
152 | "\\lceil"
153 | "\\rceil"
154 | "<"
155 | ">"
156 | "\\langle"
157 | "\\rangle"
158 | "\\lt"
159 | "\\gt"
160 | "\\lvert"
161 | "\\rvert"
162 | "\\lVert"
163 | "\\rVert"
164 | "\\lgroup"
165 | "\\rgroup"
166 | "\\lmoustache"
167 | "\\rmoustache"
168 | "/"
169 | "\\backslash"
170 | "|"
171 | "\\vert"
172 | "\\|"
173 | "\\Vert"
174 | "\\uparrow"
175 | "\\Uparrow"
176 | "\\downarrow"
177 | "\\Downarrow"
178 | "\\updownarrow"
179 | "\\Updownarrow"
180 | "."
181 )
182}