tour_parser/file/
visitor.rs

1//! [`Visitor`] implementation via syn
2use std::rc::Rc;
3use syn::*;
4use tour_core::{Delimiter, ParseError, Parser, Result, Visitor};
5
6use super::{BlockContent, File, Import};
7use crate::{
8    ast::{Scalar, Scope, StmtTempl},
9    data::Template,
10    metadata::Metadata,
11    syntax::*,
12};
13
14macro_rules! error {
15    ($($tt:tt)*) => {
16        return Err(ParseError::Generic(format!($($tt)*)))
17    };
18}
19
20// ===== Visitor =====
21
22pub struct SynVisitor<'a> {
23    layout: Option<LayoutTempl>,
24    imports: Vec<Import>,
25    blocks: Vec<BlockContent>,
26    statics: Vec<Rc<str>>,
27    root: Vec<StmtTempl>,
28
29    /// currently open scopes
30    scopes: Vec<Scope>,
31    meta: &'a Metadata,
32}
33
34impl<'a> SynVisitor<'a> {
35    pub fn generate(meta: &Metadata) -> syn::Result<File> {
36        let source = meta.resolve_source()?;
37        let visitor = SynVisitor {
38            layout: None,
39            imports: vec![],
40            blocks: vec![],
41            statics: vec![],
42            root: vec![],
43            scopes: vec![],
44            meta,
45        };
46        let me = crate::common::error!(!Parser::new(source.as_ref(), visitor).parse());
47        let SynVisitor { layout, imports, blocks, statics, root, .. } = me;
48        Ok(File { layout, imports, blocks, statics, stmts: root })
49    }
50
51    fn stack_mut(&mut self) -> &mut Vec<StmtTempl> {
52        match self.scopes.last_mut() {
53            Some(ok) => ok.stack_mut(),
54            None => &mut self.root,
55        }
56    }
57
58    fn import(&mut self, lit_str: &LitStr) -> Result<()> {
59        self.import_only(lit_str, crate::common::name())
60    }
61
62    fn import_aliased(&mut self, alias: &UseTempl) -> Result<()> {
63        self.import_only(&alias.path, alias.ident.clone())
64    }
65
66    fn import_only(&mut self, path: &LitStr, alias: Ident) -> Result<()> {
67        let path: Rc<str> = path.value().into();
68
69        if !self.imports.iter().any(|e|e==&*path) {
70            let meta = self.meta.clone_as_import(&*path);
71            let file = match Self::generate(&meta) {
72                Ok(ok) => ok,
73                Err(err) => return Err(ParseError::Generic(err.to_string())),
74            };
75            let templ = match Template::new(alias.clone(), meta, file) {
76                Ok(ok) => ok,
77                Err(err) => error!("{err}"),
78            };
79            self.imports.push(Import { path, alias, templ });
80        }
81
82        Ok(())
83    }
84}
85
86impl Visitor<'_> for SynVisitor<'_> {
87    fn visit_static(&mut self, source: &str) -> Result<()> {
88        let index = self.statics.len().try_into().unwrap();
89
90        self.stack_mut().push(StmtTempl::Scalar(Scalar::Static {
91            value: source.into(),
92            index,
93        }));
94        self.statics.push(source.into());
95
96        Ok(())
97    }
98
99    fn visit_expr(&mut self, source: &str, delim: Delimiter) -> Result<()> {
100        let expr = match syn::parse_str(source) {
101            Ok(ok) => ok,
102            Err(err) => error!("failed to parse expr: {err}"),
103        };
104
105        match expr {
106            // ===== external reference =====
107
108            StmtSyn::Layout(new_layout) => {
109                let path = new_layout.path.clone();
110                if self.layout.replace(new_layout).is_some() {
111                    error!("cannot have 2 `extends` or `layout`");
112                }
113                self.import(&path)?;
114            },
115            StmtSyn::Use(templ) => self.import_aliased(&templ)?,
116            StmtSyn::Render(templ) => {
117                if let RenderValue::Path(lit_str) = &templ.value {
118                    self.import(lit_str)?;
119                }
120                self.stack_mut().push(StmtTempl::Scalar(Scalar::Render(templ)));
121            },
122
123            // ===== scalar =====
124
125            StmtSyn::Yield(templ) => {
126                self.stack_mut().push(StmtTempl::Scalar(Scalar::Yield(templ)));
127            },
128            StmtSyn::Item(item) => {
129                self.stack_mut().push(StmtTempl::Scalar(Scalar::Item(item)));
130            },
131            StmtSyn::Expr(expr) => {
132                self.stack_mut().push(StmtTempl::Scalar(Scalar::Expr { expr, delim, }));
133            },
134
135            // ===== open scope =====
136
137            StmtSyn::Block(templ) => {
138                self.scopes.push(Scope::Block { templ, stmts: vec![] });
139            },
140            StmtSyn::If(templ) => {
141                self.scopes.push(Scope::If { templ, stmts: vec![], else_branch: None, });
142            },
143            StmtSyn::For(templ) => {
144                self.scopes.push(Scope::For { templ, stmts: vec![], else_branch: None, });
145            },
146
147            // ===== else / intermediate scope =====
148
149            StmtSyn::Else(ElseTempl { else_token, elif_branch }) => {
150                type ElseBranch = Option<(Token![else], Box<Scope>)>;
151
152                fn take_latest_else_branch(else_branch: &mut ElseBranch) -> Result<&mut ElseBranch> {
153                    match else_branch {
154                        Some((_, branch)) => match &mut **branch {
155                            // previously `else if`, we can fill more else branches
156                            Scope::If { else_branch, .. } => take_latest_else_branch(else_branch),
157                            Scope::Root { .. } => error!("cannot have 2 `else`"),
158                            _ => panic!("`else` scope can only contain Root or If"),
159                        },
160                        // if current else branch is not filled, meancs its the latest
161                        None => Ok(else_branch),
162                    }
163                }
164
165                match self.scopes.pop() {
166                    // else in if scope
167                    Some(Scope::If { templ, stmts, mut else_branch, }) => {
168                        take_latest_else_branch(&mut else_branch)?.replace((
169                            else_token,
170                            match elif_branch {
171                                Some((if_token, cond)) => Scope::If {
172                                    templ: IfTempl { if_token, cond },
173                                    stmts: vec![],
174                                    else_branch: None,
175                                },
176                                None => Scope::Root { stmts: vec![] },
177                            }
178                            .into(),
179                        ));
180
181                        self.scopes.push(Scope::If { templ, stmts, else_branch });
182                    }
183                    // else in for scope
184                    Some(Scope::For { templ, stmts, mut else_branch, }) => {
185                        let dupl = else_branch.replace(
186                            (else_token, Scope::Root { stmts: vec![] }.into())
187                        );
188                        if dupl.is_some() {
189                            error!("cannot have 2 `else` in `for` scope")
190                        }
191                        self.scopes.push(Scope::For { templ, stmts, else_branch });
192                    }
193                    Some(scope) => error!("cannot close `else` in `{scope}` scope"),
194                    None => error!("cannot close `else` in toplevel"),
195                };
196            },
197
198            // ===== close scope =====
199
200            StmtSyn::Endblock(_endblock) => {
201                let (templ,stmts) = match self.scopes.pop() {
202                    Some(Scope::Block { templ, stmts }) => (templ,stmts),
203                    Some(scope) => error!("cannot close `endblock` in `{scope}` scope"),
204                    None => error!("cannot close `endblock` in toplevel"),
205                };
206
207                let name = templ.name.clone();
208
209                if templ.static_token.is_none() {
210                    self.stack_mut().push(StmtTempl::Scalar(Scalar::Render(
211                        RenderTempl {
212                            render_token: <_>::default(),
213                            value: RenderValue::Ident(name),
214                            block: None,
215                        },
216                    )));
217                }
218
219                self.blocks.push(BlockContent { templ, stmts });
220            },
221            StmtSyn::EndIf(_endif) => {
222                let if_scope = match self.scopes.pop() {
223                    Some(templ @ Scope::If { .. }) => templ,
224                    Some(scope) => error!("cannot close `endif` in `{scope}` scope"),
225                    None => error!("cannot close `endif` in toplevel"),
226                };
227
228                self.stack_mut().push(StmtTempl::Scope(if_scope));
229            },
230            StmtSyn::EndFor(_endfor) => {
231                let for_scope = match self.scopes.pop() {
232                    Some(templ @ Scope::For { .. }) => templ,
233                    Some(scope) => error!("cannot close `endfor` in `{scope}` scope"),
234                    None => error!("cannot close `endfor` in toplevel"),
235                };
236
237                self.stack_mut().push(StmtTempl::Scope(for_scope));
238            },
239        }
240
241        Ok(())
242    }
243
244    fn finish(mut self) -> Result<Self> {
245        if let Some(scope) = self.scopes.pop() {
246            error!("unclosed `{scope}` scope")
247        }
248
249        Ok(self)
250    }
251}
252
253impl std::fmt::Display for Scope {
254    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
255        match self {
256            Self::Root { .. } => f.write_str("root"),
257            Self::Block { .. } => f.write_str("block"),
258            Self::If { .. } => f.write_str("if"),
259            Self::For { .. } => f.write_str("for"),
260        }
261    }
262}