1use 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
20pub 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 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 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 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 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 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 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 None => Ok(else_branch),
162 }
163 }
164
165 match self.scopes.pop() {
166 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 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 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}