Merge pull request #30 from Nemo157/match
Add support for a match keyword
This commit is contained in:
commit
b738d9d533
3 changed files with 174 additions and 1 deletions
|
@ -1,5 +1,6 @@
|
|||
use std::mem;
|
||||
use syntax::ast::{Expr, ExprParen, Lit, Stmt, TokenTree};
|
||||
use std::rc::Rc;
|
||||
use syntax::ast::{Expr, ExprParen, Lit, Stmt, TokenTree, Delimited};
|
||||
use syntax::ext::quote::rt::ToTokens;
|
||||
use syntax::codemap::Span;
|
||||
use syntax::errors::{DiagnosticBuilder, FatalError};
|
||||
|
@ -44,6 +45,12 @@ macro_rules! question {
|
|||
macro_rules! semi {
|
||||
() => (TokenTree::Token(_, Token::Semi))
|
||||
}
|
||||
macro_rules! comma {
|
||||
() => (TokenTree::Token(_, Token::Comma))
|
||||
}
|
||||
macro_rules! fat_arrow {
|
||||
() => (TokenTree::Token(_, Token::FatArrow))
|
||||
}
|
||||
macro_rules! minus {
|
||||
() => (TokenTree::Token(_, Token::BinOp(BinOpToken::Minus)))
|
||||
}
|
||||
|
@ -165,6 +172,11 @@ impl<'cx, 'i> Parser<'cx, 'i> {
|
|||
self.shift(2);
|
||||
try!(self.for_expr(sp));
|
||||
},
|
||||
// Match
|
||||
[at!(), keyword!(sp, k), ..] if k.is_keyword(Keyword::Match) => {
|
||||
self.shift(2);
|
||||
try!(self.match_expr(sp));
|
||||
},
|
||||
// Call
|
||||
[at!(), ident!(sp, name), ..] if name.name.as_str() == "call" => {
|
||||
self.shift(2);
|
||||
|
@ -300,6 +312,100 @@ impl<'cx, 'i> Parser<'cx, 'i> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses and renders a `@match` expression.
|
||||
///
|
||||
/// The leading `@match` should already be consumed.
|
||||
fn match_expr(&mut self, sp: Span) -> PResult<()> {
|
||||
// Parse the initial match
|
||||
let mut match_var = vec![];
|
||||
let match_bodies;
|
||||
loop { match self.input {
|
||||
[TokenTree::Delimited(sp, ref d), ..] if d.delim == DelimToken::Brace => {
|
||||
self.shift(1);
|
||||
match_bodies = try!(Parser {
|
||||
in_attr: self.in_attr,
|
||||
input: &d.tts,
|
||||
span: sp,
|
||||
render: self.render.fork(),
|
||||
}.match_bodies());
|
||||
break;
|
||||
},
|
||||
[ref tt, ..] => {
|
||||
self.shift(1);
|
||||
match_var.push(tt.clone());
|
||||
},
|
||||
[] => parse_error!(self, sp, "expected body for this @match"),
|
||||
}}
|
||||
let match_var = try!(self.with_rust_parser(match_var, RustParser::parse_expr));
|
||||
self.render.emit_match(match_var, match_bodies);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn match_bodies(&mut self) -> PResult<Vec<TokenTree>> {
|
||||
let mut bodies = Vec::new();
|
||||
loop {
|
||||
match self.input {
|
||||
[] => break,
|
||||
[ref tt @ comma!(), ..] => {
|
||||
self.shift(1);
|
||||
bodies.push(tt.clone());
|
||||
},
|
||||
[TokenTree::Token(sp, _), ..] | [TokenTree::Delimited(sp, _), ..] | [TokenTree::Sequence(sp, _), ..] => {
|
||||
bodies.append(&mut try!(self.match_body(sp)));
|
||||
},
|
||||
}
|
||||
}
|
||||
Ok(bodies)
|
||||
}
|
||||
|
||||
fn match_body(&mut self, sp: Span) -> PResult<Vec<TokenTree>> {
|
||||
let mut body = vec![];
|
||||
loop { match self.input {
|
||||
[ref tt @ fat_arrow!(), ..] => {
|
||||
self.shift(1);
|
||||
body.push(tt.clone());
|
||||
break;
|
||||
},
|
||||
[ref tt, ..] => {
|
||||
self.shift(1);
|
||||
body.push(tt.clone());
|
||||
},
|
||||
_ => parse_error!(self, sp, "invalid @match pattern"),
|
||||
}}
|
||||
let mut expr = Vec::new();
|
||||
loop { match self.input {
|
||||
[TokenTree::Delimited(sp, ref d), ..] if d.delim == DelimToken::Brace => {
|
||||
if expr.is_empty() {
|
||||
self.shift(1);
|
||||
expr = try!(self.block(sp, &d.tts)).to_tokens(self.render.cx);
|
||||
break;
|
||||
} else {
|
||||
self.shift(1);
|
||||
expr.push(TokenTree::Delimited(sp, d.clone()));
|
||||
}
|
||||
},
|
||||
[comma!(), ..] | [] => {
|
||||
if expr.is_empty() {
|
||||
parse_error!(self, sp, "expected body for this @match arm");
|
||||
} else {
|
||||
expr = try!(self.block(sp, &expr)).to_tokens(self.render.cx);
|
||||
break;
|
||||
}
|
||||
},
|
||||
[ref tt, ..] => {
|
||||
self.shift(1);
|
||||
expr.push(tt.clone());
|
||||
},
|
||||
}}
|
||||
body.push(TokenTree::Delimited(sp, Rc::new(Delimited {
|
||||
delim: DelimToken::Brace,
|
||||
open_span: sp,
|
||||
tts: expr,
|
||||
close_span: sp,
|
||||
})));
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
/// Parses and renders a `^splice`.
|
||||
///
|
||||
/// The leading `^` should already be consumed.
|
||||
|
|
|
@ -172,6 +172,11 @@ impl<'cx> Renderer<'cx> {
|
|||
self.push(stmt);
|
||||
}
|
||||
|
||||
pub fn emit_match(&mut self, match_var: P<Expr>, match_body: Vec<TokenTree>) {
|
||||
let stmt = quote_stmt!(self.cx, match $match_var { $match_body }).unwrap();
|
||||
self.push(stmt);
|
||||
}
|
||||
|
||||
pub fn emit_call(&mut self, func: P<Expr>) {
|
||||
let w = self.writer;
|
||||
let expr = quote_expr!(self.cx, ($func)(&mut *$w));
|
||||
|
|
|
@ -250,6 +250,68 @@ mod control {
|
|||
"<li>Sweetie Belle</li>",
|
||||
"</ul>"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_expr() {
|
||||
for &(input, output) in [(Some("yay"), "<div>yay</div>"), (None, "oh noes")].iter() {
|
||||
let mut s = String::new();
|
||||
html!(s, {
|
||||
@match input {
|
||||
Some(value) => {
|
||||
div { ^value }
|
||||
},
|
||||
None => {
|
||||
"oh noes"
|
||||
},
|
||||
}
|
||||
}).unwrap();
|
||||
assert_eq!(s, output);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_expr_without_delims() {
|
||||
for &(input, output) in [(Some("yay"), "yay"), (None, "<span>oh noes</span>")].iter() {
|
||||
let mut s = String::new();
|
||||
html!(s, {
|
||||
@match input {
|
||||
Some(value) => ^value,
|
||||
None => span { "oh noes" },
|
||||
}
|
||||
}).unwrap();
|
||||
assert_eq!(s, output);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_expr_with_guards() {
|
||||
for &(input, output) in [(Some(1), "one"), (None, "none"), (Some(2), "2")].iter() {
|
||||
let mut s = String::new();
|
||||
html!(s, {
|
||||
@match input {
|
||||
Some(value) if value == 1 => "one",
|
||||
Some(value) => ^value,
|
||||
None => "none",
|
||||
}
|
||||
}).unwrap();
|
||||
assert_eq!(s, output);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn match_in_attribute() {
|
||||
for &(input, output) in [(1, "<span class=\"one\">1</span>"), (2, "<span class=\"two\">2</span>"), (3, "<span class=\"many\">3</span>")].iter() {
|
||||
let mut s = String::new();
|
||||
html!(s, {
|
||||
span class=@match input {
|
||||
1 => "one",
|
||||
2 => "two",
|
||||
_ => "many",
|
||||
} { ^input }
|
||||
}).unwrap();
|
||||
assert_eq!(s, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Add table
Reference in a new issue