org_rust_parser/object/
inline_src.rs

1use crate::constants::{LBRACE, LBRACK, NEWLINE, RBRACE, RBRACK};
2use crate::node_pool::NodeID;
3use crate::types::{Cursor, MatchError, ParseOpts, Parseable, Parser, Result};
4use crate::utils::Match;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct InlineSrc<'a> {
8    pub lang: &'a str,
9    pub headers: Option<&'a str>,
10    pub body: &'a str,
11}
12
13impl<'a> Parseable<'a> for InlineSrc<'a> {
14    fn parse(
15        parser: &mut Parser<'a>,
16        mut cursor: Cursor<'a>,
17        parent: Option<NodeID>,
18        parse_opts: ParseOpts,
19    ) -> Result<NodeID> {
20        let start = cursor.index;
21        cursor.word("src_")?;
22
23        let lang =
24            cursor.fn_until(|chr: u8| chr == b'[' || chr == b'{' || chr.is_ascii_whitespace())?;
25
26        cursor.index = lang.end;
27
28        match cursor.curr() {
29            LBRACE => {
30                let body = Self::parse_body(cursor)?;
31                Ok(parser.alloc(
32                    Self {
33                        lang: lang.obj,
34                        headers: None,
35                        body: body.obj,
36                    },
37                    start,
38                    body.end,
39                    None,
40                ))
41            }
42            LBRACK => {
43                let header = Self::parse_header(cursor)?;
44                cursor.move_to(header.end);
45                if cursor.curr() == LBRACE {
46                    let body = Self::parse_body(cursor)?;
47                    Ok(parser.alloc(
48                        Self {
49                            lang: lang.obj,
50                            headers: Some(header.obj),
51                            body: body.obj,
52                        },
53                        start,
54                        body.end,
55                        parent,
56                    ))
57                } else {
58                    Err(MatchError::InvalidLogic)
59                }
60            }
61            // We are whitespace here, which means there was whitespace after the src_
62            // so blow up
63            _ => Err(MatchError::InvalidLogic),
64        }
65    }
66}
67
68impl<'a> InlineSrc<'a> {
69    // the logic is exactly the same, except for the perimeters
70    fn parse_header(cursor: Cursor) -> Result<Match<&str>> {
71        InlineSrc::parse_src(cursor, LBRACK, RBRACK)
72    }
73    fn parse_body(cursor: Cursor) -> Result<Match<&str>> {
74        InlineSrc::parse_src(cursor, LBRACE, RBRACE)
75    }
76    #[inline(always)]
77    fn parse_src(mut cursor: Cursor, lperim: u8, rperim: u8) -> Result<Match<&str>> {
78        // Brackets have to be balanced
79        // -1 for left bracket
80        // +1 for right bracket
81        let mut bracket_count: i32 = 0;
82
83        let start = cursor.index;
84        loop {
85            match cursor.curr() {
86                chr if chr == lperim => {
87                    bracket_count -= 1;
88                }
89                chr if chr == rperim => {
90                    bracket_count += 1;
91                    if bracket_count == 0 {
92                        return Ok(Match {
93                            start,
94                            // +1 to skip past lperim and rperim
95                            end: cursor.index + 1,
96                            obj: cursor.clamp_backwards(start + 1),
97                        });
98                    }
99                }
100                NEWLINE => {
101                    return Err(MatchError::InvalidLogic);
102                }
103                _ => {}
104            } // end of match
105
106            cursor.next();
107        } // end of loop
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use crate::expr_in_pool;
114    use crate::object::InlineSrc;
115    use crate::parse_org;
116    use crate::types::Expr;
117    use pretty_assertions::assert_eq;
118
119    #[test]
120    fn basic_src() {
121        let input = "src_python{neat}";
122
123        let parsed = parse_org(input);
124        let l = expr_in_pool!(parsed, InlineSrc).unwrap();
125
126        assert_eq!(
127            l,
128            &InlineSrc {
129                lang: "python",
130                headers: None,
131                body: "neat"
132            }
133        )
134    }
135
136    #[test]
137    fn inlinesrc_header() {
138        let input = "src_python[fun]{rad}";
139
140        let parsed = parse_org(input);
141        let l = expr_in_pool!(parsed, InlineSrc).unwrap();
142
143        assert_eq!(
144            l,
145            &InlineSrc {
146                lang: "python",
147                headers: Some("fun"),
148                body: "rad"
149            }
150        )
151    }
152}