Skip to main content

ratex_parser/functions/
left_right.rs

1use std::collections::HashMap;
2
3use crate::error::{ParseError, ParseResult};
4use crate::functions::{define_function_full, FunctionContext, FunctionSpec};
5use crate::parse_node::ParseNode;
6
7pub fn register(map: &mut HashMap<&'static str, FunctionSpec>) {
8    define_function_full(
9        map,
10        &["\\left"],
11        "leftright",
12        1,
13        0,
14        None,
15        false,
16        false,
17        true,
18        false,
19        true, // primitive
20        handle_left,
21    );
22
23    define_function_full(
24        map,
25        &["\\right"],
26        "leftright-right",
27        1,
28        0,
29        None,
30        false,
31        false,
32        true,
33        false,
34        true, // primitive
35        handle_right,
36    );
37
38    define_function_full(
39        map,
40        &["\\middle"],
41        "middle",
42        1,
43        0,
44        None,
45        false,
46        false,
47        true,
48        false,
49        true,
50        handle_middle,
51    );
52}
53
54fn handle_left(
55    ctx: &mut FunctionContext,
56    args: Vec<ParseNode>,
57    _opt_args: Vec<Option<ParseNode>>,
58) -> ParseResult<ParseNode> {
59    let delim = get_delim_text(&args[0])?;
60
61    ctx.parser.leftright_depth += 1;
62    let body = ctx.parser.parse_expression(false, None)?;
63    ctx.parser.leftright_depth -= 1;
64
65    // Expect \right, but don't consume it yet
66    ctx.parser.expect("\\right", false)?;
67    // Parse \right as a function, which returns a leftright-right node
68    let right_node = ctx.parser.parse_function(None, None)?;
69
70    let (right, right_color) = match right_node {
71        Some(ParseNode::LeftRightRight { delim, color, .. }) => (delim, color),
72        _ => {
73            return Err(ParseError::msg("Expected \\right after \\left"));
74        }
75    };
76
77    Ok(ParseNode::LeftRight {
78        mode: ctx.parser.mode,
79        body,
80        left: delim,
81        right,
82        right_color,
83        loc: None,
84    })
85}
86
87fn handle_right(
88    ctx: &mut FunctionContext,
89    args: Vec<ParseNode>,
90    _opt_args: Vec<Option<ParseNode>>,
91) -> ParseResult<ParseNode> {
92    let delim = get_delim_text(&args[0])?;
93
94    Ok(ParseNode::LeftRightRight {
95        mode: ctx.parser.mode,
96        delim,
97        color: None,
98        loc: None,
99    })
100}
101
102fn handle_middle(
103    ctx: &mut FunctionContext,
104    args: Vec<ParseNode>,
105    _opt_args: Vec<Option<ParseNode>>,
106) -> ParseResult<ParseNode> {
107    let delim = get_delim_text(&args[0])?;
108
109    if ctx.parser.leftright_depth <= 0 {
110        return Err(ParseError::msg(
111            "\\middle must be within \\left and \\right",
112        ));
113    }
114
115    Ok(ParseNode::Middle {
116        mode: ctx.parser.mode,
117        delim,
118        loc: None,
119    })
120}
121
122fn get_delim_text(node: &ParseNode) -> ParseResult<String> {
123    if let Some(text) = node.symbol_text() {
124        return Ok(text.to_string());
125    }
126    Err(ParseError::msg(format!(
127        "Invalid delimiter type '{}'",
128        node.type_name(),
129    )))
130}