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