diff --git a/maud_macros/src/parse.rs b/maud_macros/src/parse.rs index bc2a948..26c1460 100644 --- a/maud_macros/src/parse.rs +++ b/maud_macros/src/parse.rs @@ -102,6 +102,11 @@ impl<'cx, 's, 'i> Parser<'cx, 's, 'i> { self.shift(2); self.if_expr(sp); }, + // For + [dollar!(), ident!(sp, name), ..] if name.as_str() == "for" => { + self.shift(2); + self.for_expr(sp); + }, // Splice [ref tt @ dollar!(), dollar!(), ..] => { self.shift(2); @@ -192,6 +197,38 @@ impl<'cx, 's, 'i> Parser<'cx, 's, 'i> { self.render.emit_if(if_cond, if_body, else_body); } + fn for_expr(&mut self, sp: Span) { + let mut pattern = vec![]; + loop { match self.input { + [ident!(in_), ..] if in_.as_str() == "in" => { + self.shift(1); + break; + }, + [ref tt, ..] => { + self.shift(1); + pattern.push(tt.clone()); + }, + _ => self.render.cx.span_fatal(sp, "invalid $for"), + }} + let pattern = self.new_rust_parser(pattern).parse_pat(); + let mut iterable = vec![]; + let body; + loop { match self.input { + [TtDelimited(sp, ref d), ..] if d.delim == DelimToken::Brace => { + self.shift(1); + body = self.block(sp, &d.tts); + break; + }, + [ref tt, ..] => { + self.shift(1); + iterable.push(tt.clone()); + }, + _ => self.render.cx.span_fatal(sp, "invalid $for"), + }} + let iterable = self.new_rust_parser(iterable).parse_expr(); + self.render.emit_for(pattern, iterable, body); + } + fn splice(&mut self, sp: Span) -> P<Expr> { let mut tts = vec![]; // First, munch a single token tree diff --git a/maud_macros/src/render.rs b/maud_macros/src/render.rs index f415950..20b9810 100644 --- a/maud_macros/src/render.rs +++ b/maud_macros/src/render.rs @@ -1,4 +1,4 @@ -use syntax::ast::{Expr, Ident, Stmt}; +use syntax::ast::{Expr, Ident, Pat, Stmt}; use syntax::ext::base::ExtCtxt; use syntax::ext::build::AstBuilder; use syntax::parse::token; @@ -135,4 +135,9 @@ impl<'cx, 's> Renderer<'cx, 's> { }; self.stmts.push(stmt); } + + pub fn emit_for(&mut self, pattern: P<Pat>, iterable: P<Expr>, body: Vec<P<Stmt>>) { + let stmt = quote_stmt!(self.cx, for $pattern in $iterable { $body }); + self.stmts.push(stmt); + } } diff --git a/maud_macros/tests/tests.rs b/maud_macros/tests/tests.rs index d322049..4ce1763 100644 --- a/maud_macros/tests/tests.rs +++ b/maud_macros/tests/tests.rs @@ -201,4 +201,20 @@ mod control { assert_eq!(s, name); } } + + #[test] + fn for_expr() { + let ponies = ["Apple Bloom", "Scootaloo", "Sweetie Belle"]; + let s = html! { + ul $for pony in &ponies { + li $pony + } + }.to_string(); + assert_eq!(s, concat!( + "<ul>", + "<li>Apple Bloom</li>", + "<li>Scootaloo</li>", + "<li>Sweetie Belle</li>", + "</ul>")); + } }