intuicio_parser/
template.rs

1use crate::{ParseResult, Parser, ParserExt, ParserHandle, ParserOutput, ParserRegistry};
2use regex::{Captures, Regex};
3
4pub mod shorthand {
5    use super::*;
6
7    pub fn template(
8        parser: ParserHandle,
9        rule: Option<String>,
10        content: impl ToString,
11    ) -> ParserHandle {
12        TemplateParser::new(parser, rule, content).into_handle()
13    }
14}
15
16thread_local! {
17    static REGEX: Regex = Regex::new(r"@(>|<)\{([^\}]*)\}\[([^\]@]*)\]\{([^\}]*)\}(\[(\d+)\])?@").unwrap();
18}
19
20pub struct TemplateParser {
21    parser: ParserHandle,
22    rule: Option<String>,
23    content: String,
24}
25
26impl TemplateParser {
27    pub fn new(parser: ParserHandle, rule: Option<String>, content: impl ToString) -> Self {
28        Self {
29            parser,
30            rule,
31            content: content.to_string(),
32        }
33    }
34}
35
36impl Parser for TemplateParser {
37    fn parse<'a>(&self, registry: &ParserRegistry, input: &'a str) -> ParseResult<'a> {
38        let (input, result) = self.parser.parse(registry, input)?;
39        let content =
40            if let Some(value) = result.read::<String>() {
41                self.content.replace("@{}@", &value)
42            } else if let Some(list) = result.read::<Vec<ParserOutput>>() {
43                REGEX.with(|regex| {
44                    regex
45                        .replace_all(&self.content, |caps: &Captures| -> String {
46                            let ordering = caps.get(1).unwrap().as_str();
47                            let prefix = caps.get(2).unwrap().as_str();
48                            let delimiter = caps.get(3).unwrap().as_str();
49                            let suffix = caps.get(4).unwrap().as_str();
50                            let mut result = String::default();
51                            if let Some(index) = caps.get(6) {
52                                let index = index.as_str().parse::<usize>().unwrap();
53                                result.push_str(prefix);
54                                let item = list
55                            .get(index)
56                            .unwrap_or_else(|| {
57                                panic!("Template parsing result list has no item at {index} index!")
58                            })
59                            .read::<String>()
60                            .unwrap_or_else(|| {
61                                panic!("Template parsing result list item {index} is not String!")
62                            });
63                                result.push_str(item.as_str());
64                                result.push_str(suffix);
65                            } else if ordering == ">" {
66                                for (index, item) in list.iter().enumerate() {
67                                    if index > 0 {
68                                        result.push_str(delimiter);
69                                    }
70                                    result.push_str(prefix);
71                                    let item = item.read::<String>().unwrap_or_else(|| {
72                                panic!("Template parsing result list item {index} is not String!")
73                            });
74                                    result.push_str(item.as_str());
75                                    result.push_str(suffix);
76                                }
77                            } else if ordering == "<" {
78                                for (index, item) in list.iter().rev().enumerate() {
79                                    if index > 0 {
80                                        result.push_str(delimiter);
81                                    }
82                                    result.push_str(prefix);
83                                    let item = item.read::<String>().unwrap_or_else(|| {
84                                panic!("Template parsing result list item {index} is not String!")
85                            });
86                                    result.push_str(item.as_str());
87                                    result.push_str(suffix);
88                                }
89                            }
90                            result
91                        })
92                        .to_string()
93                })
94            } else {
95                return Err("Template parsing result is not String or Vec<ParserOutput>!".into());
96            };
97        if let Some(rule) = self.rule.as_ref() {
98            let (rest, result) = registry.parse(rule, &content)?;
99            if rest.is_empty() {
100                Ok((input, result))
101            } else {
102                Err("Templating content parsing did not consumed all source!".into())
103            }
104        } else {
105            Ok((input, ParserOutput::new(content).ok().unwrap()))
106        }
107    }
108
109    fn extend(&self, parser: ParserHandle) {
110        self.parser.extend(parser);
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use crate::{
117        ParserOutput, ParserRegistry,
118        shorthand::{inject, lit, map, number_int, prefix, seq_del, source, template, ws},
119        template::TemplateParser,
120    };
121
122    fn is_async<T: Send + Sync>() {}
123
124    #[test]
125    fn test_template() {
126        is_async::<TemplateParser>();
127
128        let registry = ParserRegistry::default()
129            .with_parser(
130                "value",
131                map(prefix(number_int(), lit("value:")), |value: String| {
132                    value.parse::<i32>().unwrap()
133                }),
134            )
135            .with_parser(
136                "add",
137                map(
138                    seq_del(lit("+"), [inject("value"), inject("value")]),
139                    |mut values: Vec<ParserOutput>| {
140                        let b = values.remove(1).consume::<i32>().ok().unwrap();
141                        let a = values.remove(0).consume::<i32>().ok().unwrap();
142                        a + b
143                    },
144                ),
145            )
146            .with_parser(
147                "sub",
148                map(
149                    seq_del(lit("-"), [inject("value"), inject("value")]),
150                    |mut values: Vec<ParserOutput>| {
151                        let b = values.remove(1).consume::<i32>().ok().unwrap();
152                        let a = values.remove(0).consume::<i32>().ok().unwrap();
153                        a - b
154                    },
155                ),
156            )
157            .with_parser(
158                "mul",
159                map(
160                    seq_del(lit("*"), [inject("value"), inject("value")]),
161                    |mut values: Vec<ParserOutput>| {
162                        let b = values.remove(1).consume::<i32>().ok().unwrap();
163                        let a = values.remove(0).consume::<i32>().ok().unwrap();
164                        a * b
165                    },
166                ),
167            )
168            .with_parser(
169                "template_value",
170                template(source(number_int()), Some("value".to_owned()), "value:@{}@"),
171            )
172            .with_parser(
173                "template_add",
174                template(
175                    seq_del(
176                        ws(),
177                        [
178                            source(inject("template_value")),
179                            source(inject("template_value")),
180                        ],
181                    ),
182                    Some("add".to_owned()),
183                    "@>{value:}[+]{}@",
184                ),
185            )
186            .with_parser(
187                "template_sub",
188                template(
189                    seq_del(
190                        ws(),
191                        [
192                            source(inject("template_value")),
193                            source(inject("template_value")),
194                        ],
195                    ),
196                    Some("sub".to_owned()),
197                    "@<{value:}[-]{}@",
198                ),
199            )
200            .with_parser(
201                "template_mul",
202                template(
203                    seq_del(
204                        ws(),
205                        [
206                            source(inject("template_value")),
207                            source(inject("template_value")),
208                        ],
209                    ),
210                    Some("mul".to_owned()),
211                    "value:@>{}[]{}[0]@*value:@>{}[]{}[1]@",
212                ),
213            )
214            .with_parser(
215                "template_output",
216                template(source(inject("template_value")), None, "#@{}@"),
217            );
218
219        let (rest, result) = registry.parse("value", "value:42").unwrap();
220        assert_eq!(rest, "");
221        assert_eq!(result.consume::<i32>().ok().unwrap(), 42);
222
223        let (rest, result) = registry.parse("add", "value:40+value:2").unwrap();
224        assert_eq!(rest, "");
225        assert_eq!(result.consume::<i32>().ok().unwrap(), 42);
226
227        let (rest, result) = registry.parse("sub", "value:40-value:2").unwrap();
228        assert_eq!(rest, "");
229        assert_eq!(result.consume::<i32>().ok().unwrap(), 38);
230
231        let (rest, result) = registry.parse("mul", "value:6*value:4").unwrap();
232        assert_eq!(rest, "");
233        assert_eq!(result.consume::<i32>().ok().unwrap(), 24);
234
235        let (rest, result) = registry.parse("template_value", "42").unwrap();
236        assert_eq!(rest, "");
237        assert_eq!(result.consume::<i32>().ok().unwrap(), 42);
238
239        let (rest, result) = registry.parse("template_add", "40 2").unwrap();
240        assert_eq!(rest, "");
241        assert_eq!(result.consume::<i32>().ok().unwrap(), 42);
242
243        let (rest, result) = registry.parse("template_sub", "2 40").unwrap();
244        assert_eq!(rest, "");
245        assert_eq!(result.consume::<i32>().ok().unwrap(), 38);
246
247        let (rest, result) = registry.parse("template_mul", "6 4").unwrap();
248        assert_eq!(rest, "");
249        assert_eq!(result.consume::<i32>().ok().unwrap(), 24);
250
251        let (rest, result) = registry.parse("template_output", "42").unwrap();
252        assert_eq!(rest, "");
253        assert_eq!(result.consume::<String>().ok().unwrap(), "#42");
254    }
255}