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