dysql_tpl/template/
parse.rs

1// Ramhorns  Copyright (C) 2019  Maciej Hirsz
2//
3// This file is part of Ramhorns. This program comes with ABSOLUTELY NO WARRANTY;
4// This is free software, and you are welcome to redistribute it under the
5// conditions of the GNU General Public License version 3.0.
6//
7// You should have received a copy of the GNU General Public License
8// along with Ramhorns.  If not, see <http://www.gnu.org/licenses/>
9
10use arrayvec::ArrayVec;
11use logos::Logos;
12use serde::{Serialize, Deserialize};
13
14use super::{hash_name, Block, TemplateError, Template};
15use crate::Partials;
16
17#[derive(Debug, PartialEq, Clone, Copy, Default)]
18pub struct ParseError;
19
20
21// r"[^{]+": 跳过非 { 的字符,跳过 \{ 转移的字符
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Logos)]
23#[logos(
24    skip r"[^{]+", 
25    skip r"\{",
26    extras = Braces,
27    error = ParseError,
28)]
29#[derive(Serialize, Deserialize)]
30pub enum Tag {
31    /// `{{escaped}}` tag
32    #[token("{{")]
33    Escaped,
34
35    /// `{{{unescaped}}}` tag
36    #[token("{{&")]
37    #[token("{{{", |lex| lex.extras = Braces::Three)]
38    Unescaped,
39
40    /// `{{#section}}` opening tag (with number of subsequent blocks it contains)
41    #[token("{{#")]
42    Section,
43
44    /// `{{^inverse}}` section opening tag (with number of subsequent blocks it contains)
45    #[token("{{^")]
46    Inverse,
47
48    /// `{{/closing}}` section tag
49    #[token("{{/")]
50    Closing,
51
52    /// `{{!comment}}` tag
53    #[token("{{!")]
54    Comment,
55
56    // /// `{{>partial}}` tag
57    // #[token("{{>")]
58    // Partial,
59
60    /// `{{?not_none}}` tag
61    #[token("{{?")]
62    NotNone,
63
64    /// Tailing html
65    Tail,
66}
67
68impl From<ParseError> for TemplateError {
69    fn from(_: ParseError) -> TemplateError {
70        TemplateError::UnclosedTag
71    }
72}
73
74#[derive(Logos)]
75#[logos(
76    skip r"[. ]+",
77    extras = Braces,
78)]
79#[derive(Debug)]
80enum Closing {
81    #[token("}}", |lex| {
82        // 如果开始的是 {{ 而结尾的 是 }}} ,则报错
83        // Force fail the match if we expected 3 braces
84        lex.extras != Braces::Three
85    })]
86    #[token("}}}")]
87    Match,
88
89    #[regex(r"[^. \}]+")]
90    Ident,
91}
92
93/// Marker of how many braces we expect to match
94#[derive(PartialEq, Eq, Clone, Copy)]
95pub enum Braces {
96    Two = 2,
97    Three = 3,
98}
99
100impl Default for Braces {
101    #[inline]
102    fn default() -> Self {
103        Braces::Two
104    }
105}
106
107impl Template {
108    pub(crate) fn parse(
109        &mut self,
110        source: &str,
111        _partials: &mut impl Partials,
112    ) -> Result<usize, TemplateError> {
113        let mut last = 0;
114        let mut lex = Tag::lexer(source);
115        let mut stack = ArrayVec::<usize, 16>::new();
116
117        while let Some(tag) = lex.next() {
118            // 起始 token
119            let tag = tag?;
120
121            // Grab HTML from before the token
122            // TODO: add lex.before() that yields source slice
123            // in front of the token:
124            //
125            // let html = &lex.before()[last..];
126            let mut html = &lex.source()[last..lex.span().start];
127            self.capacity_hint += html.len();
128
129            // Morphing the lexer to match the closing
130            // braces and grab the name
131            let mut closing = lex.morph();
132            let tail_idx = self.blocks.len();
133
134            // 获取标识符
135            let _tok = closing.next();
136            if !matches!(Some(Closing::Ident), _tok) {
137                return Err(TemplateError::UnclosedTag);
138            }
139            // 可能是标识符名 或 结束token
140            let mut name = closing.slice();
141
142            match tag {
143                Tag::Escaped | Tag::Unescaped => {
144                    loop {
145                        match closing.next() {
146                            // 如果起始为 {{ ,在标识符后又找到空格分隔的下一个标识符
147                            Some(Ok(Closing::Ident)) => {
148                                self.blocks.push(Block::new(html, name, Tag::Section));
149                                name = closing.slice();
150                                html = "";
151                            }
152                            // 如果起始为 {{ 则,找到 }}
153                            Some(Ok(Closing::Match)) => {
154                                self.blocks.push(Block::new(html, name, tag));
155                                break;
156                            }
157                            _ => return Err(TemplateError::UnclosedTag),
158                        }
159                    }
160                    
161                    // block 中本次新增元素数
162                    let d = self.blocks.len() - tail_idx - 1;
163
164                    for i in 0..d {
165                        self.blocks[tail_idx + i].children = (d - i) as u32; //children: 2 1 0 递减
166                    }
167                }
168                Tag::Section | Tag::Inverse => loop {
169                    // 如果起始为 {{# ,在标识符后又找到空格分隔的下一个标识符
170                    match closing.next() {
171                        Some(Ok(Closing::Ident)) => {
172                            // 记录上一层的 block 元素数
173                            stack.try_push(self.blocks.len())?;
174                            self.blocks.push(Block::new(html, name, Tag::Section));
175                            name = closing.slice();
176                            html = "";
177                        }
178                        Some(Ok(Closing::Match)) => {
179                            // }} 时 记录本层的 block 元素数
180                            stack.try_push(self.blocks.len())?;
181                            self.blocks.push(Block::new(html, name, tag));
182                            break;
183                        }
184                        _ => return Err(TemplateError::UnclosedTag),
185                    }
186                },
187                Tag::NotNone => loop {
188                    match closing.next() {
189                        Some(Ok(Closing::Ident)) => {
190                            stack.try_push(self.blocks.len())?;
191                            self.blocks.push(Block::new(html, name, Tag::NotNone));
192                            name = closing.slice();
193                            html = "";
194                        }
195                        Some(Ok(Closing::Match)) => {
196                            stack.try_push(self.blocks.len())?;
197                            self.blocks.push(Block::new(html, name, tag));
198                            break;
199                        }
200                        _ => return Err(TemplateError::UnclosedTag),
201                    }
202                }
203                Tag::Closing => {
204                    self.blocks.push(Block::nameless(html, Tag::Closing));
205
206                    let mut pop_section = |name| {
207                        let hash = hash_name(name);
208
209                        let head_idx = stack
210                            .pop()
211                            .ok_or_else(|| TemplateError::UnopenedSection(name.into()))?;
212
213                        let head = &mut self.blocks[head_idx];
214                        head.children = (tail_idx - head_idx) as u32;
215
216                        if head.hash != hash {
217                            return Err(TemplateError::UnclosedSection(head.name.clone()));
218                        }
219                        Ok(())
220                    };
221
222                    let mut tmp = vec![name];
223                    loop {
224                        match closing.next() {
225                            Some(Ok(Closing::Ident)) => {
226                                tmp.push(closing.slice());
227                            }
228                            Some(Ok(Closing::Match)) => break,
229                            _ => return Err(TemplateError::UnclosedTag),
230                        }
231                    }
232
233                    let t_len = tmp.len();
234                    for i in 0..t_len {
235                        pop_section(tmp[t_len - i - 1])?;
236                    }
237
238                    // pop_section(name)?;
239                    // loop {
240                    //     match closing.next() {
241                    //         Some(Ok(Closing::Ident)) => {
242                    //             pop_section(closing.slice())?;
243                    //         }
244                    //         Some(Ok(Closing::Match)) => break,
245                    //         _ => return Err(Error::UnclosedTag),
246                    //     }
247                    // }
248                }
249                // Tag::Partial => {
250                //     match closing.next() {
251                //         Some(Ok(Closing::Match)) => {}
252                //         _ => return Err(TemplateError::UnclosedTag),
253                //     }
254
255                //     self.blocks.push(Block::nameless(html, tag));
256                //     let partial = partials.get_partial(name)?;
257                //     self.blocks.extend_from_slice(&partial.blocks);
258                //     self.capacity_hint += partial.capacity_hint;
259                // }
260                _ => {
261                    loop {
262                        match closing.next() {
263                            Some(Ok(Closing::Ident)) => continue,
264                            Some(Ok(Closing::Match)) => break,
265                            _ => return Err(TemplateError::UnclosedTag),
266                        }
267                    }
268                    self.blocks.push(Block::nameless(html, tag));
269                }
270            };
271
272            // Add the number of braces that we were expecting,
273            // not the number we got:
274            //
275            // `{{foo}}}` should not consume the last `}`
276            last = closing.span().start + closing.extras as usize;
277            lex = closing.morph();
278            lex.extras = Braces::Two;
279        }
280
281        Ok(last)
282    }
283}
284
285#[cfg(test)]
286mod tests {
287    use crate::Template;
288
289    #[test]
290    fn test() {
291        let s = "
292        {{?t1}}
293            {{#t1}}
294                {{name}}abcd
295            {{/t1}}
296        {{/t1}}
297        ";
298        let _tpl = Template::new(s).unwrap();
299    }
300}