org_rust_parser/element/
latex_env.rs

1use crate::constants::{NEWLINE, RBRACE, STAR};
2use crate::node_pool::NodeID;
3use crate::types::{Cursor, MatchError, ParseOpts, Parseable, Parser, Result};
4use regex::bytes::Regex;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct LatexEnv<'a> {
8    pub name: &'a str,
9    pub contents: &'a str,
10}
11
12impl<'a> Parseable<'a> for LatexEnv<'a> {
13    fn parse(
14        parser: &mut Parser<'a>,
15        mut cursor: Cursor<'a>,
16        parent: Option<NodeID>,
17        parse_opts: ParseOpts,
18    ) -> Result<NodeID> {
19        let start = cursor.index;
20        cursor.word(r"\begin{")?;
21        let name_match = cursor.fn_until(|chr| {
22            !chr.is_ascii_alphanumeric() && chr != STAR || (chr == NEWLINE || chr == RBRACE)
23        })?;
24
25        cursor.index = name_match.end;
26        cursor.word("}\n")?;
27        let name = name_match.obj;
28
29        let a = format!(r"(?m)^[ \t]*\\end{{{name}}}[\t ]*$");
30        // HACK/FIXME: i simply cannot figure out how to properly escape the curls in the regex.
31        // always getting hit with:
32        // error: repetition quantifier expects a valid decimal
33        let a = a.replace("{", r"\{");
34        let a = a.replace("}", r"\}");
35
36        let ending_re: Regex = Regex::new(&a).unwrap();
37        let matched_reg = ending_re
38            .find_at(cursor.byte_arr, cursor.index)
39            .ok_or(MatchError::InvalidLogic)?;
40
41        Ok(parser.alloc(
42            Self {
43                name,
44                contents: cursor.clamp_forwards(matched_reg.start()),
45            },
46            start,
47            matched_reg.end(),
48            parent,
49        ))
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use crate::{element::LatexEnv, expr_in_pool, parse_org, types::Expr};
56
57    #[test]
58    fn basic_latex_env() {
59        let inp = r"
60\begin{align*}
61\end{align*}
62";
63
64        dbg!(parse_org(inp));
65    }
66
67    #[test]
68    fn latex_env_with_content() {
69        let inp = r"
70\begin{align*}
71
72\text{latex constructs}\\
73\alpha\\
74\beta\\
75
7610x + 4 &= 3\\
77
78\end{align*}
79";
80
81        dbg!(parse_org(inp));
82    }
83
84    #[test]
85    fn latex_env_failed_header() {
86        // not alpha numeric
87        let inp = r"
88\begin{star!}
89\end{star!}
90";
91
92        dbg!(parse_org(inp));
93
94        let inp = r"
95\begin{a13214-}
96\end{a13214-}
97";
98        dbg!(parse_org(inp));
99        // failed construction
100        let inp = r"
101\begin{one}more stuff
102\end{one}
103";
104        dbg!(parse_org(inp));
105    }
106
107    #[test]
108    fn latex_empty_start() {
109        let inp = r"
110\begin{}
111\end{}
112";
113        dbg!(parse_org(inp));
114    }
115
116    #[test]
117    fn latex_failed_end() {
118        let inp = r"
119\begin{start}
120\end{notstart}
121";
122        dbg!(parse_org(inp));
123    }
124
125    #[test]
126    fn latex_env_indented() {
127        let input = r"
128             \begin{align}
129             we are eating so good?
130             \end{align}
131";
132
133        let parsed = parse_org(input);
134
135        let l = expr_in_pool!(parsed, LatexEnv).unwrap();
136
137        assert_eq!(
138            l,
139            &LatexEnv {
140                name: "align",
141                contents: "             we are eating so good?\n"
142            }
143        )
144    }
145}