From 553a6bc037b6800926207f20ede8468454b424a4 Mon Sep 17 00:00:00 2001 From: Utkarsh Kukreti Date: Wed, 19 Oct 2016 21:43:11 +0530 Subject: [PATCH] Add support for `@while` and `@while let`. Fixes #51. --- maud_macros/src/parse.rs | 27 +++++++++++++++++++++++++ maud_macros/src/render.rs | 9 +++++++++ maud_macros/tests/control_structures.rs | 22 ++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/maud_macros/src/parse.rs b/maud_macros/src/parse.rs index b2b888f4..c42f5e50 100644 --- a/maud_macros/src/parse.rs +++ b/maud_macros/src/parse.rs @@ -142,6 +142,11 @@ impl<'cx, 'a, 'i> Parser<'cx, 'a, 'i> { self.shift(2); self.if_expr(sp)?; }, + // While + [at!(), keyword!(sp, k), ..] if k.is_keyword(keywords::While) => { + self.shift(2); + self.while_expr(sp)?; + }, // For [at!(), keyword!(sp, k), ..] if k.is_keyword(keywords::For) => { self.shift(2); @@ -248,6 +253,28 @@ impl<'cx, 'a, 'i> Parser<'cx, 'a, 'i> { Ok(()) } + /// Parses and renders an `@while` expression. + /// + /// The leading `@while` should already be consumed. + fn while_expr(&mut self, sp: Span) -> PResult<()> { + let mut cond = vec![]; + let body; + loop { match *self.input { + [TokenTree::Delimited(sp, ref d), ..] if d.delim == DelimToken::Brace => { + self.shift(1); + body = self.block(sp, &d.tts)?; + break; + }, + [ref tt, ..] => { + self.shift(1); + cond.push(tt.clone()); + }, + [] => parse_error!(self, sp, "expected body for this @while"), + }} + self.render.emit_while(cond, body); + Ok(()) + } + /// Parses and renders a `@for` expression. /// /// The leading `@for` should already be consumed. diff --git a/maud_macros/src/render.rs b/maud_macros/src/render.rs index 6efbc699..f1b034f4 100644 --- a/maud_macros/src/render.rs +++ b/maud_macros/src/render.rs @@ -138,6 +138,15 @@ impl<'cx, 'a> Renderer<'cx, 'a> { self.push(stmt); } + /// Emits an `while` expression. + /// + /// The condition is a token tree (not an expression) so we don't + /// need to special-case `while let`. + pub fn emit_while(&mut self, cond: Vec, body: Vec) { + let stmt = quote_stmt!(self.cx, while $cond { $body }).unwrap(); + self.push(stmt); + } + pub fn emit_for(&mut self, pattern: P, iterable: P, body: Vec) { let stmt = quote_stmt!(self.cx, for $pattern in $iterable { $body }).unwrap(); self.push(stmt); diff --git a/maud_macros/tests/control_structures.rs b/maud_macros/tests/control_structures.rs index 32a3166f..1f4332f0 100644 --- a/maud_macros/tests/control_structures.rs +++ b/maud_macros/tests/control_structures.rs @@ -35,6 +35,28 @@ fn if_let() { } } +#[test] +fn while_expr() { + let mut numbers = (0..3).into_iter().peekable(); + let s = html! { + ul @while numbers.peek().is_some() { + li (numbers.next().unwrap()) + } + }.into_string(); + assert_eq!(s, "
  • 0
  • 1
  • 2
"); +} + +#[test] +fn while_let_expr() { + let mut numbers = (0..3).into_iter(); + let s = html! { + ul @while let Some(n) = numbers.next() { + li (n) + } + }.into_string(); + assert_eq!(s, "
  • 0
  • 1
  • 2
"); +} + #[test] fn for_expr() { let ponies = ["Apple Bloom", "Scootaloo", "Sweetie Belle"];