use proc_macro::{ Delimiter, Literal, Spacing, Span, TokenNode, TokenStream, TokenTree, TokenTreeIter, }; use std::iter; use std::mem; use literalext::LiteralExt; use super::render::Renderer; use super::ParseResult; pub fn parse(input: TokenStream) -> ParseResult<TokenStream> { // Heuristic: the size of the resulting markup tends to correlate with the // code size of the template itself let size_hint = input.to_string().len(); let mut render = Renderer::new(); Parser { in_attr: false, input: input.into_iter(), }.markups(&mut render)?; Ok(render.into_expr(size_hint)) } #[derive(Clone)] struct Parser { in_attr: bool, input: TokenTreeIter, } impl Iterator for Parser { type Item = TokenTree; fn next(&mut self) -> Option<TokenTree> { self.input.next() } } impl Parser { fn peek(&mut self) -> Option<TokenTree> { self.clone().next() } fn peek2(&mut self) -> Option<(TokenTree, Option<TokenTree>)> { let mut clone = self.clone(); clone.next().map(|first| (first, clone.next())) } fn advance(&mut self) { self.next(); } fn advance2(&mut self) { self.next(); self.next(); } fn commit(&mut self, attempt: Parser) { *self = attempt; } /// Returns an `Err` with the given message. fn error<T, E: Into<String>>(&self, message: E) -> ParseResult<T> { Err(message.into()) } /// Parses and renders multiple blocks of markup. fn markups(&mut self, render: &mut Renderer) -> ParseResult<()> { loop { match self.peek() { None => return Ok(()), Some(TokenTree { kind: TokenNode::Op(';', _), .. }) => self.advance(), _ => self.markup(render)?, } } } /// Parses and renders a single block of markup. fn markup(&mut self, render: &mut Renderer) -> ParseResult<()> { let token = match self.peek() { Some(token) => token, None => return self.error("unexpected end of input"), }; match token { // Literal TokenTree { kind: TokenNode::Literal(lit), .. } => { self.advance(); self.literal(lit, render)?; }, // Special form TokenTree { kind: TokenNode::Op('@', _), .. } => { self.advance(); match self.next() { Some(TokenTree { kind: TokenNode::Term(term), .. }) => match term.as_str() { "if" => self.if_expr(render)?, "while" => self.while_expr(render)?, "for" => self.for_expr(render)?, "match" => self.match_expr(render)?, "let" => self.let_expr(render)?, other => return self.error(format!("unknown keyword `@{}`", other)), }, _ => return self.error("expected keyword after `@`"), } } // Element TokenTree { kind: TokenNode::Term(_), .. } => { let name = self.namespaced_name()?; self.element(&name, render)?; }, // Splice TokenTree { kind: TokenNode::Group(Delimiter::Parenthesis, expr), .. } => { self.advance(); render.splice(expr); } // Block TokenTree { kind: TokenNode::Group(Delimiter::Brace, block), .. } => { self.advance(); Parser { in_attr: self.in_attr, input: block.into_iter(), }.markups(render)?; }, // ??? _ => return self.error("invalid syntax"), } Ok(()) } /// Parses and renders a literal string. fn literal(&mut self, lit: Literal, render: &mut Renderer) -> ParseResult<()> { if let Some(s) = lit.parse_string() { render.string(&s); Ok(()) } else { self.error("expected string") } } /// Parses and renders an `@if` expression. /// /// The leading `@if` should already be consumed. fn if_expr(&mut self, render: &mut Renderer) -> ParseResult<()> { let mut if_cond = Vec::new(); let if_body = loop { match self.next() { Some(TokenTree { kind: TokenNode::Group(Delimiter::Brace, block), .. }) => { break self.block(block, render)?; }, Some(token) => if_cond.push(token), None => return self.error("unexpected end of @if expression"), } }; let else_body = match self.peek2() { // Try to match an `@else` after this Some(( TokenTree { kind: TokenNode::Op('@', _), .. }, Some(TokenTree { kind: TokenNode::Term(else_keyword), .. }), )) if else_keyword.as_str() == "else" => { self.advance2(); match self.peek() { // `@else if` Some(TokenTree { kind: TokenNode::Term(if_keyword), .. }) if if_keyword.as_str() == "if" => { self.advance(); let mut render = render.fork(); self.if_expr(&mut render)?; Some(render.into_stmts()) }, // Just an `@else` _ => { if let Some(TokenTree { kind: TokenNode::Group(Delimiter::Brace, block), .. }) = self.next() { Some(self.block(block, render)?) } else { return self.error("expected body for @else"); } }, } }, _ => { // We didn't find an `@else`; backtrack None }, }; render.emit_if(if_cond.into_iter().collect(), if_body, else_body); Ok(()) } /// Parses and renders an `@while` expression. /// /// The leading `@while` should already be consumed. fn while_expr(&mut self, render: &mut Renderer) -> ParseResult<()> { let mut cond = Vec::new(); let body = loop { match self.next() { Some(TokenTree { kind: TokenNode::Group(Delimiter::Brace, block), .. }) => { break self.block(block, render)?; }, Some(token) => cond.push(token), None => return self.error("unexpected end of @while expression"), } }; render.emit_while(cond.into_iter().collect(), body); Ok(()) } /// Parses and renders a `@for` expression. /// /// The leading `@for` should already be consumed. fn for_expr(&mut self, render: &mut Renderer) -> ParseResult<()> { let mut pat = Vec::new(); loop { match self.next() { Some(TokenTree { kind: TokenNode::Term(in_keyword), .. }) if in_keyword.as_str() == "in" => break, Some(token) => pat.push(token), None => return self.error("unexpected end of @for expression"), } } let mut expr = Vec::new(); let body = loop { match self.next() { Some(TokenTree { kind: TokenNode::Group(Delimiter::Brace, block), .. }) => { break self.block(block, render)?; }, Some(token) => expr.push(token), None => return self.error("unexpected end of @for expression"), } }; render.emit_for(pat.into_iter().collect(), expr.into_iter().collect(), body); Ok(()) } /// Parses and renders a `@match` expression. /// /// The leading `@match` should already be consumed. fn match_expr(&mut self, render: &mut Renderer) -> ParseResult<()> { let mut head = Vec::new(); let body = loop { match self.next() { Some(TokenTree { kind: TokenNode::Group(Delimiter::Brace, body), .. }) => { let mut parse = Parser { in_attr: self.in_attr, input: body.into_iter(), }; break parse.match_arms(render)?; }, Some(token) => head.push(token), None => return self.error("unexpected end of @match expression"), } }; render.emit_match(head.into_iter().collect(), body); Ok(()) } fn match_arms(&mut self, render: &mut Renderer) -> ParseResult<TokenStream> { let mut arms = Vec::new(); while let Some(arm) = self.match_arm(render)? { arms.push(arm); } Ok(arms.into_iter().collect()) } fn match_arm(&mut self, render: &mut Renderer) -> ParseResult<Option<TokenStream>> { let mut pat = Vec::new(); loop { match self.peek2() { Some(( eq @ TokenTree { kind: TokenNode::Op('=', Spacing::Joint), .. }, Some(gt @ TokenTree { kind: TokenNode::Op('>', _), .. }), )) => { self.advance2(); pat.push(eq); pat.push(gt); break; }, Some((token, _)) => { self.advance(); pat.push(token); }, None => if pat.is_empty() { return Ok(None); } else { return self.error("unexpected end of @match pattern"); }, } } let body = match self.next() { // $pat => { $stmts } Some(TokenTree { kind: TokenNode::Group(Delimiter::Brace, body), span }) => { let body = self.block(body, render)?; // Trailing commas are optional if the match arm is a braced block if let Some(TokenTree { kind: TokenNode::Op(',', _), .. }) = self.peek() { self.advance(); } // Re-use the span from the original block TokenTree { kind: TokenNode::Group(Delimiter::Brace, body), span, } }, // $pat => $expr Some(first_token) => { let mut body = vec![first_token]; loop { match self.next() { Some(TokenTree { kind: TokenNode::Op(',', _), .. }) => break, Some(token) => { body.push(token); }, None => return self.error("unexpected end of @match arm"), } } let body = self.block(body.into_iter().collect(), render)?; // The generated code may have multiple statements, unlike the // original expression. So wrap the whole thing in a block just // in case. TokenTree { kind: TokenNode::Group(Delimiter::Brace, body), span: Span::default(), } }, None => return self.error("unexpected end of @match arm"), }; Ok(Some(pat.into_iter().chain(iter::once(body)).collect())) } /// Parses and renders a `@let` expression. /// /// The leading `@let` should already be consumed. fn let_expr(&mut self, render: &mut Renderer) -> ParseResult<()> { let mut pat = Vec::new(); loop { match self.next() { Some(TokenTree { kind: TokenNode::Op('=', _), .. }) => break, Some(token) => pat.push(token), None => return self.error("unexpected end of @let expression"), } } let mut expr = Vec::new(); let body = loop { match self.next() { Some(TokenTree { kind: TokenNode::Group(Delimiter::Brace, block), .. }) => { break self.block(block, render)?; }, Some(token) => expr.push(token), None => return self.error("unexpected end of @let expression"), } }; render.emit_let(pat.into_iter().collect(), expr.into_iter().collect(), body); Ok(()) } /// Parses and renders an element node. /// /// The element name should already be consumed. fn element(&mut self, name: &str, render: &mut Renderer) -> ParseResult<()> { if self.in_attr { return self.error("unexpected element, you silly bumpkin"); } render.element_open_start(name); self.attrs(render)?; render.element_open_end(); match self.peek() { Some(TokenTree { kind: TokenNode::Op('/', _), .. }) => { // Void element self.advance(); }, _ => { self.markup(render)?; render.element_close(name); }, } Ok(()) } /// Parses and renders the attributes of an element. fn attrs(&mut self, render: &mut Renderer) -> ParseResult<()> { let mut classes_static = Vec::new(); let mut classes_toggled = Vec::new(); let mut ids = Vec::new(); loop { let mut attempt = self.clone(); let maybe_name = attempt.namespaced_name(); let token_after = attempt.next(); match (maybe_name, token_after) { // Non-empty attribute (Ok(name), Some(TokenTree { kind: TokenNode::Op('=', _), .. })) => { self.commit(attempt); render.attribute_start(&name); { // Parse a value under an attribute context let in_attr = mem::replace(&mut self.in_attr, true); self.markup(render)?; self.in_attr = in_attr; } render.attribute_end(); }, // Empty attribute (Ok(name), Some(TokenTree { kind: TokenNode::Op('?', _), .. })) => { self.commit(attempt); match self.peek() { // Toggle the attribute based on a boolean expression Some(TokenTree { kind: TokenNode::Group(Delimiter::Bracket, cond), .. }) => { self.advance(); let body = { let mut render = render.fork(); render.attribute_empty(&name); render.into_stmts() }; render.emit_if(cond, body, None); }, // Write the attribute unconditionally _ => render.attribute_empty(&name), } }, // Class shorthand (Err(_), Some(TokenTree { kind: TokenNode::Op('.', _), .. })) => { self.commit(attempt); let class_name = self.name()?; match self.peek() { // Toggle the class based on a boolean expression Some(TokenTree { kind: TokenNode::Group(Delimiter::Bracket, cond), .. }) => { self.advance(); classes_toggled.push((cond, class_name)); }, // Emit the class unconditionally _ => classes_static.push(class_name), } }, // ID shorthand (Err(_), Some(TokenTree { kind: TokenNode::Op('#', _), .. })) => { self.commit(attempt); ids.push(self.name()?); }, // If it's not a valid attribute, backtrack and bail out _ => break, } } if !classes_static.is_empty() || !classes_toggled.is_empty() { render.attribute_start("class"); render.string(&classes_static.join(" ")); for (i, (cond, mut class_name)) in classes_toggled.into_iter().enumerate() { // If a class comes first in the list, then it shouldn't be // prefixed by a space if i > 0 || !classes_static.is_empty() { class_name = format!(" {}", class_name); } let body = { let mut render = render.fork(); render.string(&class_name); render.into_stmts() }; render.emit_if(cond, body, None); } render.attribute_end(); } if !ids.is_empty() { render.attribute_start("id"); render.string(&ids.join(" ")); render.attribute_end(); } Ok(()) } /// Parses an identifier, without dealing with namespaces. fn name(&mut self) -> ParseResult<String> { let mut s = if let Some(TokenTree { kind: TokenNode::Term(term), .. }) = self.peek() { self.advance(); String::from(term.as_str()) } else { return self.error("expected identifier"); }; let mut expect_ident = false; loop { expect_ident = match self.peek() { Some(TokenTree { kind: TokenNode::Op('-', _), .. }) => { self.advance(); s.push('-'); true }, Some(TokenTree { kind: TokenNode::Term(term), .. }) if expect_ident => { self.advance(); s.push_str(term.as_str()); false }, _ => break, }; } Ok(s) } /// Parses a HTML element or attribute name, along with a namespace /// if necessary. fn namespaced_name(&mut self) -> ParseResult<String> { let mut s = self.name()?; if let Some(TokenTree { kind: TokenNode::Op(':', _), .. }) = self.peek() { self.advance(); s.push(':'); s.push_str(&self.name()?); } Ok(s) } /// Parses the given token tree, returning a vector of statements. fn block(&mut self, body: TokenStream, render: &mut Renderer) -> ParseResult<TokenStream> { let mut render = render.fork(); let mut parse = Parser { in_attr: self.in_attr, input: body.into_iter(), }; parse.markups(&mut render)?; Ok(render.into_stmts()) } }