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 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::ext::quote::rt::ToTokens;
|
||||||
use syntax::codemap::Span;
|
use syntax::codemap::Span;
|
||||||
use syntax::errors::{DiagnosticBuilder, FatalError};
|
use syntax::errors::{DiagnosticBuilder, FatalError};
|
||||||
|
@ -44,6 +45,12 @@ macro_rules! question {
|
||||||
macro_rules! semi {
|
macro_rules! semi {
|
||||||
() => (TokenTree::Token(_, Token::Semi))
|
() => (TokenTree::Token(_, Token::Semi))
|
||||||
}
|
}
|
||||||
|
macro_rules! comma {
|
||||||
|
() => (TokenTree::Token(_, Token::Comma))
|
||||||
|
}
|
||||||
|
macro_rules! fat_arrow {
|
||||||
|
() => (TokenTree::Token(_, Token::FatArrow))
|
||||||
|
}
|
||||||
macro_rules! minus {
|
macro_rules! minus {
|
||||||
() => (TokenTree::Token(_, Token::BinOp(BinOpToken::Minus)))
|
() => (TokenTree::Token(_, Token::BinOp(BinOpToken::Minus)))
|
||||||
}
|
}
|
||||||
|
@ -165,6 +172,11 @@ impl<'cx, 'i> Parser<'cx, 'i> {
|
||||||
self.shift(2);
|
self.shift(2);
|
||||||
try!(self.for_expr(sp));
|
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
|
// Call
|
||||||
[at!(), ident!(sp, name), ..] if name.name.as_str() == "call" => {
|
[at!(), ident!(sp, name), ..] if name.name.as_str() == "call" => {
|
||||||
self.shift(2);
|
self.shift(2);
|
||||||
|
@ -300,6 +312,100 @@ impl<'cx, 'i> Parser<'cx, 'i> {
|
||||||
Ok(())
|
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`.
|
/// Parses and renders a `^splice`.
|
||||||
///
|
///
|
||||||
/// The leading `^` should already be consumed.
|
/// The leading `^` should already be consumed.
|
||||||
|
|
|
@ -172,6 +172,11 @@ impl<'cx> Renderer<'cx> {
|
||||||
self.push(stmt);
|
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>) {
|
pub fn emit_call(&mut self, func: P<Expr>) {
|
||||||
let w = self.writer;
|
let w = self.writer;
|
||||||
let expr = quote_expr!(self.cx, ($func)(&mut *$w));
|
let expr = quote_expr!(self.cx, ($func)(&mut *$w));
|
||||||
|
|
|
@ -250,6 +250,68 @@ mod control {
|
||||||
"<li>Sweetie Belle</li>",
|
"<li>Sweetie Belle</li>",
|
||||||
"</ul>"));
|
"</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]
|
#[test]
|
||||||
|
|
Loading…
Add table
Reference in a new issue