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}