diff options
-rw-r--r-- | askama/src/generator.rs | 52 | ||||
-rw-r--r-- | askama/src/parser.rs | 23 | ||||
-rw-r--r-- | testing/templates/for.html | 3 | ||||
-rw-r--r-- | testing/tests/simple.rs | 15 |
4 files changed, 89 insertions, 4 deletions
diff --git a/askama/src/generator.rs b/askama/src/generator.rs index 95e4dcb..c70e507 100644 --- a/askama/src/generator.rs +++ b/askama/src/generator.rs @@ -1,16 +1,23 @@ -use parser::{Conds, Expr, Node}; +use parser::{Conds, Expr, Node, Nodes, Target}; use std::str; +use std::collections::HashSet; struct Generator { buf: String, indent: u8, start: bool, + locals: HashSet<String>, } impl Generator { fn new() -> Generator { - Generator { buf: String::new(), indent: 0, start: true } + Generator { + buf: String::new(), + indent: 0, + start: true, + locals: HashSet::new(), + } } fn init(&mut self, name: &str) { @@ -51,7 +58,12 @@ impl Generator { } fn visit_var(&mut self, s: &[u8]) { - self.write(&format!("self.{}", str::from_utf8(s).unwrap())); + let s = str::from_utf8(s).unwrap(); + if self.locals.contains(s) { + self.write(&format!("{}", s)); + } else { + self.write(&format!("self.{}", s)); + } } fn visit_filter(&mut self, name: &str, val: &Expr) { @@ -67,6 +79,16 @@ impl Generator { } } + fn visit_target_single(&mut self, name: &[u8]) -> Vec<String> { + vec![str::from_utf8(name).unwrap().to_string()] + } + + fn visit_target(&mut self, target: &Target) -> Vec<String> { + match target { + &Target::Name(s) => { self.visit_target_single(s) }, + } + } + fn write_lit(&mut self, s: &[u8]) { self.write("buf.push_str("); self.write(&format!("{:#?}", str::from_utf8(s).unwrap())); @@ -100,12 +122,36 @@ impl Generator { self.writeln("}"); } + fn write_loop(&mut self, var: &Target, iter: &Expr, body: &Nodes) { + + self.write("for "); + let targets = self.visit_target(var); + for name in &targets { + self.locals.insert(name.clone()); + self.write(&format!("{}", name)); + } + self.write(" in &"); + self.visit_expr(iter); + self.writeln(" {"); + + self.indent(); + self.handle(body); + self.dedent(); + self.writeln("}"); + for name in &targets { + self.locals.remove(name); + } + } + fn handle(&mut self, tokens: &Vec<Node>) { for n in tokens { match n { &Node::Lit(val) => { self.write_lit(val); }, &Node::Expr(ref val) => { self.write_expr(&val); }, &Node::Cond(ref conds) => { self.write_cond(&conds); }, + &Node::Loop(ref var, ref iter, ref body) => { + self.write_loop(&var, &iter, &body); + }, } } } diff --git a/askama/src/parser.rs b/askama/src/parser.rs index 96f123c..2e94ef4 100644 --- a/askama/src/parser.rs +++ b/askama/src/parser.rs @@ -6,10 +6,15 @@ pub enum Expr<'a> { Filter(&'a str, Box<Expr<'a>>), } +pub enum Target<'a> { + Name(&'a [u8]), +} + pub enum Node<'a> { Lit(&'a [u8]), Expr(Expr<'a>), Cond(Vec<(Option<Expr<'a>>, Vec<Node<'a>>)>), + Loop(Target<'a>, Expr<'a>, Vec<Node<'a>>), } pub type Nodes<'a> = Vec<Node<'a>>; @@ -35,6 +40,8 @@ fn take_content(i: &[u8]) -> IResult<&[u8], Node> { named!(expr_var<Expr>, map!(nom::alphanumeric, Expr::Var)); +named!(target_single<Target>, map!(nom::alphanumeric, Target::Name)); + fn expr_filtered(i: &[u8]) -> IResult<&[u8], Expr> { let (mut left, mut expr) = match expr_var(i) { IResult::Error(err) => { return IResult::Error(err); }, @@ -92,10 +99,24 @@ named!(block_if<Node>, do_parse!( Node::Cond(res) }))); +named!(block_for<Node>, do_parse!( + tag_s!("{%") >> + ws!(tag_s!("for")) >> + var: ws!(target_single) >> + ws!(tag_s!("in")) >> + iter: ws!(expr_filtered) >> + tag_s!("%}") >> + block: parse_template >> + tag_s!("{%") >> + ws!(tag_s!("endfor")) >> + tag_s!("%}") >> + (Node::Loop(var, iter, block)))); + named!(parse_template<Nodes>, many1!(alt!( take_content | expr_node | - block_if))); + block_if | + block_for))); pub fn parse<'a>(src: &'a str) -> Nodes { match parse_template(src.as_bytes()) { diff --git a/testing/templates/for.html b/testing/templates/for.html new file mode 100644 index 0000000..c38e6bb --- /dev/null +++ b/testing/templates/for.html @@ -0,0 +1,3 @@ +{% for s in strings %} + {{ s }} +{% endfor %} diff --git a/testing/tests/simple.rs b/testing/tests/simple.rs index b8274c3..ab5e209 100644 --- a/testing/tests/simple.rs +++ b/testing/tests/simple.rs @@ -64,3 +64,18 @@ fn test_else_if() { let s = ElseIfTemplate { cond: false, check: true }; assert_eq!(s.render(), "checked\n"); } + + +#[derive(Template)] +#[template(path = "for.html")] +struct ForTemplate { + strings: Vec<String>, +} + +#[test] +fn test_for() { + let s = ForTemplate { + strings: vec!["A".to_string(), "alfa".to_string(), "1".to_string()], + }; + assert_eq!(s.render(), "\n A\n\n alfa\n\n 1\n\n"); +} |