implement basic match functionality

This commit is contained in:
Anthony Nowell 2017-09-27 01:53:35 -06:00 committed by Dirkjan Ochtman
parent 14beb21d0c
commit 468f376bfc
4 changed files with 119 additions and 1 deletions

View File

@ -1,6 +1,6 @@
use filters;
use input::TemplateInput;
use parser::{self, Cond, Expr, Macro, Node, Target, WS};
use parser::{self, Cond, Expr, Macro, Node, Target, When, WS};
use path;
use quote::{Tokens, ToTokens};
@ -339,6 +339,9 @@ impl<'a> Generator<'a> {
Node::Cond(ref conds, ref ws) => {
self.write_cond(state, conds, ws);
},
Node::Match(ref ws1, ref expr, ref inter, ref arms, ref ws2) => {
self.write_match(state, ws1, expr, inter, arms, ws2);
},
Node::Loop(ref ws1, ref var, ref iter, ref body, ref ws2) => {
self.write_loop(state, ws1, var, iter, body, ws2);
},
@ -428,6 +431,43 @@ impl<'a> Generator<'a> {
self.writeln("}");
}
fn write_match(&mut self, state: &'a State, ws1: &WS, expr: &Expr, inter: &'a str, arms:
&'a [When], ws2: &WS) {
self.flush_ws(ws1);
if !inter.is_empty() {
self.next_ws = Some(inter);
}
self.write("match ");
self.visit_expr(expr);
self.writeln(" {");
for arm in arms {
let &(ref ws, ref variant, ref params, ref body) = arm;
self.locals.push();
self.write(variant);
if params.len() > 0 {
self.write("(");
for (i, param) in params.iter().enumerate() {
self.locals.insert(param);
if i > 0 {
self.write(", ");
}
self.write(param);
}
self.write(")");
}
self.writeln(" => {");
self.handle_ws(ws);
self.handle(state, body, AstLevel::Nested);
self.writeln("}");
self.locals.pop();
}
self.writeln("}");
self.handle_ws(ws2);
}
fn write_loop(&mut self, state: &'a State, ws1: &WS, var: &'a Target, iter: &Expr,
body: &'a [Node], ws2: &WS) {
self.handle_ws(ws1);

View File

@ -40,6 +40,7 @@ pub enum Node<'a> {
LetDecl(WS, Target<'a>),
Let(WS, Target<'a>, Expr<'a>),
Cond(Vec<(WS, Option<Expr<'a>>, Vec<Node<'a>>)>, WS),
Match(WS, Expr<'a>, &'a str, Vec<When<'a>>, WS),
Loop(WS, Target<'a>, Expr<'a>, Vec<Node<'a>>, WS),
Extends(Expr<'a>),
BlockDef(WS, &'a str, Vec<Node<'a>>, WS),
@ -49,6 +50,7 @@ pub enum Node<'a> {
}
pub type Cond<'a> = (WS, Option<Expr<'a>>, Vec<Node<'a>>);
pub type When<'a> = (WS, &'a str, Vec<&'a str>, Vec<Node<'a>>);
fn split_ws_parts(s: &[u8]) -> Node {
if s.is_empty() {
@ -210,6 +212,12 @@ named!(parameters<Vec<&'a str>>, do_parse!(
(vals.unwrap_or_default())
));
named!(with_parameters<Vec<&'a str>>, do_parse!(
tag_s!("with") >>
params: ws!(parameters) >>
(params)
));
named!(expr_group<Expr>, map!(
delimited!(char!('('), expr_any, char!(')')),
|s| Expr::Group(Box::new(s))
@ -359,6 +367,51 @@ named!(block_if<Node>, do_parse!(
})
));
named!(when_block<When>, do_parse!(
tag_s!("{%") >>
pws: opt!(tag_s!("-")) >>
ws!(tag_s!("when")) >>
variant: ws!(identifier) >>
params: opt!(ws!(with_parameters)) >>
nws: opt!(tag_s!("-")) >>
tag_s!("%}") >>
block: parse_template >>
(WS(pws.is_some(), nws.is_some()), variant, params.unwrap_or_default(), block)
));
named!(block_match<Node>, do_parse!(
pws1: opt!(tag_s!("-")) >>
ws!(tag_s!("match")) >>
expr: ws!(expr_any) >>
nws1: opt!(tag_s!("-")) >>
tag_s!("%}") >>
inter: take_content >>
arms: many1!(when_block) >>
ws!(tag_s!("{%")) >>
pws2: opt!(tag_s!("-")) >>
ws!(tag_s!("endmatch")) >>
nws2: opt!(tag_s!("-")) >>
({
let inter = match inter {
Node::Lit(lws, val, rws) => {
assert!(val.is_empty(),
"only whitespace allowed between match and first when, found {}", val);
assert!(rws.is_empty(),
"only whitespace allowed between match and first when, found {}", rws);
lws
},
_ => panic!("only literals allowed between match and first when"),
};
Node::Match(
WS(pws1.is_some(), nws1.is_some()),
expr,
inter,
arms,
WS(pws2.is_some(), nws2.is_some()),
)
})
));
named!(block_let<Node>, do_parse!(
pws: opt!(tag_s!("-")) >>
ws!(tag_s!("let")) >>
@ -471,6 +524,7 @@ named!(block_node<Node>, do_parse!(
block_let |
block_if |
block_for |
block_match |
block_extends |
block_include |
block_import |

View File

@ -0,0 +1,6 @@
{% match item %}
{% when Some with (val) %}
Found {{val}}
{% when None %}
Not Found
{% endmatch %}

18
testing/tests/matches.rs Normal file
View File

@ -0,0 +1,18 @@
#[macro_use]
extern crate askama;
use askama::Template;
#[derive(Template)]
#[template(path = "match.html")]
struct MatchTemplate<'a> {
item: Option<&'a str>,
}
#[test]
fn test_match_option() {
let s = MatchTemplate {
item: Some("foo"),
};
assert_eq!(s.render().unwrap(), "\n\nFound foo\n");
}