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