aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--askama/src/generator.rs52
-rw-r--r--askama/src/parser.rs23
-rw-r--r--testing/templates/for.html3
-rw-r--r--testing/tests/simple.rs15
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");
+}