Implement generator support for whitespace handling markers

This commit is contained in:
Dirkjan Ochtman 2017-02-08 14:49:27 +01:00
parent 38c31ee4bd
commit 06bd6ef634
3 changed files with 118 additions and 33 deletions

View File

@ -1,4 +1,4 @@
use parser::{Cond, Expr, Node, Target}; use parser::{Cond, Expr, Node, Target, WS};
use std::str; use std::str;
use std::collections::HashSet; use std::collections::HashSet;
use syn; use syn;
@ -28,21 +28,25 @@ fn path_as_identifier(s: &str) -> String {
res res
} }
struct Generator { struct Generator<'a> {
buf: String, buf: String,
indent: u8, indent: u8,
start: bool, start: bool,
locals: HashSet<String>, locals: HashSet<String>,
next_ws: Option<&'a str>,
skip_ws: bool,
} }
impl Generator { impl<'a> Generator<'a> {
fn new() -> Generator { fn new() -> Generator<'a> {
Generator { Generator {
buf: String::new(), buf: String::new(),
indent: 0, indent: 0,
start: true, start: true,
locals: HashSet::new(), locals: HashSet::new(),
next_ws: None,
skip_ws: false,
} }
} }
@ -117,20 +121,42 @@ impl Generator {
} }
} }
fn write_lit(&mut self, lws: &str, val: &str, rws: &str) { fn flush_ws(&mut self, ws: &WS) {
self.write(&format!("writer.write_str({:#?}).unwrap();", lws)); if self.next_ws.is_some() && !ws.0 {
self.write(&format!("writer.write_str({:#?}).unwrap();", val)); let val = self.next_ws.unwrap();
self.write(&format!("writer.write_str({:#?}).unwrap();", rws)); self.write(&format!("writer.write_str({:#?}).unwrap();", val));
self.next_ws = None;
}
} }
fn write_expr(&mut self, s: &Expr) { fn prepare_ws(&mut self, ws: &WS) {
self.skip_ws = ws.1;
}
fn handle_ws(&mut self, ws: &WS) {
self.flush_ws(ws);
self.prepare_ws(ws);
}
fn write_lit(&mut self, lws: &str, val: &str, rws: &'a str) {
assert!(self.next_ws.is_none());
if !self.skip_ws {
self.write(&format!("writer.write_str({:#?}).unwrap();", lws));
}
self.write(&format!("writer.write_str({:#?}).unwrap();", val));
self.next_ws = Some(rws);
}
fn write_expr(&mut self, ws: &WS, s: &Expr) {
self.handle_ws(ws);
self.write("writer.write_fmt(format_args!(\"{}\", "); self.write("writer.write_fmt(format_args!(\"{}\", ");
self.visit_expr(s); self.visit_expr(s);
self.writeln(")).unwrap();"); self.writeln(")).unwrap();");
} }
fn write_cond(&mut self, conds: &[Cond]) { fn write_cond(&mut self, conds: &'a [Cond], ws: &WS) {
for (i, &(_, ref cond, ref nodes)) in conds.iter().enumerate() { for (i, &(ref cws, ref cond, ref nodes)) in conds.iter().enumerate() {
self.handle_ws(cws);
match *cond { match *cond {
Some(ref expr) => { Some(ref expr) => {
if i == 0 { if i == 0 {
@ -147,11 +173,14 @@ impl Generator {
self.handle(nodes); self.handle(nodes);
self.dedent(); self.dedent();
} }
self.handle_ws(ws);
self.writeln("}"); self.writeln("}");
} }
fn write_loop(&mut self, var: &Target, iter: &Expr, body: &[Node]) { fn write_loop(&mut self, ws1: &WS, var: &Target, iter: &Expr,
body: &'a [Node], ws2: &WS) {
self.handle_ws(ws1);
self.write("for "); self.write("for ");
let targets = self.visit_target(var); let targets = self.visit_target(var);
for name in &targets { for name in &targets {
@ -164,6 +193,7 @@ impl Generator {
self.indent(); self.indent();
self.handle(body); self.handle(body);
self.handle_ws(ws2);
self.dedent(); self.dedent();
self.writeln("}"); self.writeln("}");
for name in &targets { for name in &targets {
@ -171,33 +201,42 @@ impl Generator {
} }
} }
fn write_block(&mut self, name: &str) { fn write_block(&mut self, ws1: &WS, name: &str, ws2: &WS) {
self.flush_ws(ws1);
self.writeln(&format!("self.render_block_{}_into(writer);", name)); self.writeln(&format!("self.render_block_{}_into(writer);", name));
self.prepare_ws(ws2);
} }
fn write_block_def(&mut self, name: &str, nodes: &[Node]) { fn write_block_def(&mut self, ws1: &WS, name: &str, nodes: &'a [Node],
ws2: &WS) {
self.writeln("#[allow(unused_variables)]"); self.writeln("#[allow(unused_variables)]");
self.writeln(&format!( self.writeln(&format!(
"fn render_block_{}_into(&self, writer: &mut std::fmt::Write) {{", "fn render_block_{}_into(&self, writer: &mut std::fmt::Write) {{",
name)); name));
self.indent(); self.indent();
self.prepare_ws(ws1);
self.handle(nodes); self.handle(nodes);
self.flush_ws(ws2);
self.dedent(); self.dedent();
self.writeln("}"); self.writeln("}");
} }
fn handle(&mut self, nodes: &[Node]) { fn handle(&mut self, nodes: &'a [Node]) {
for n in nodes { for n in nodes {
match *n { match *n {
Node::Lit(lws, val, rws) => { self.write_lit(lws, val, rws); } Node::Lit(lws, val, rws) => { self.write_lit(lws, val, rws); }
Node::Expr(_, ref val) => { self.write_expr(val); }, Node::Expr(ref ws, ref val) => { self.write_expr(ws, val); },
Node::Cond(ref conds, _) => { self.write_cond(conds); }, Node::Cond(ref conds, ref ws) => {
Node::Loop(_, ref var, ref iter, ref body, _) => { self.write_cond(conds, ws);
self.write_loop(var, iter, body);
}, },
Node::Block(_, name, _) => { self.write_block(name) }, Node::Loop(ref ws1, ref var, ref iter, ref body, ref ws2) => {
Node::BlockDef(_, name, ref block_nodes, _) => { self.write_loop(ws1, var, iter, body, ws2);
self.write_block_def(name, block_nodes); },
Node::Block(ref ws1, name, ref ws2) => {
self.write_block(ws1, name, ws2);
},
Node::BlockDef(ref ws1, name, ref block_nodes, ref ws2) => {
self.write_block_def(ws1, name, block_nodes, ws2);
} }
Node::Extends(_) => { Node::Extends(_) => {
panic!("no extends or block definition allowed in content"); panic!("no extends or block definition allowed in content");
@ -216,7 +255,7 @@ impl Generator {
self.writeln(&s); self.writeln(&s);
} }
fn template_impl(&mut self, ast: &syn::DeriveInput, nodes: &[Node]) { fn template_impl(&mut self, ast: &syn::DeriveInput, nodes: &'a [Node]) {
let anno = annotations(&ast.generics); let anno = annotations(&ast.generics);
self.writeln(&format!("impl{} askama::Template for {}{} {{", self.writeln(&format!("impl{} askama::Template for {}{} {{",
anno, ast.ident.as_ref(), anno)); anno, ast.ident.as_ref(), anno));
@ -225,6 +264,7 @@ impl Generator {
self.writeln("fn render_into(&self, writer: &mut std::fmt::Write) {"); self.writeln("fn render_into(&self, writer: &mut std::fmt::Write) {");
self.indent(); self.indent();
self.handle(nodes); self.handle(nodes);
self.flush_ws(&WS(false, false));
self.dedent(); self.dedent();
self.writeln("}"); self.writeln("}");
@ -232,13 +272,14 @@ impl Generator {
self.writeln("}"); self.writeln("}");
} }
fn trait_impl(&mut self, ast: &syn::DeriveInput, base: &str, blocks: &[Node]) { fn trait_impl(&mut self, ast: &syn::DeriveInput, base: &str, blocks: &'a [Node]) {
let anno = annotations(&ast.generics); let anno = annotations(&ast.generics);
self.writeln(&format!("impl{} TraitFrom{} for {}{} {{", self.writeln(&format!("impl{} TraitFrom{} for {}{} {{",
anno, path_as_identifier(base), anno, path_as_identifier(base),
ast.ident.as_ref(), anno)); ast.ident.as_ref(), anno));
self.indent(); self.indent();
self.handle(blocks); self.handle(blocks);
self.flush_ws(&WS(false, false));
self.dedent(); self.dedent();
self.writeln("}"); self.writeln("}");
} }
@ -260,7 +301,7 @@ impl Generator {
} }
fn template_trait(&mut self, ast: &syn::DeriveInput, path: &str, fn template_trait(&mut self, ast: &syn::DeriveInput, path: &str,
blocks: &[Node], nodes: &[Node]) { blocks: &'a [Node], nodes: &'a [Node]) {
let anno = annotations(&ast.generics); let anno = annotations(&ast.generics);
self.writeln(&format!("trait{} TraitFrom{}{} {{", anno, self.writeln(&format!("trait{} TraitFrom{}{} {{", anno,
path_as_identifier(path), anno)); path_as_identifier(path), anno));
@ -271,6 +312,7 @@ impl Generator {
self.writeln("fn render_trait_into(&self, writer: &mut std::fmt::Write) {"); self.writeln("fn render_trait_into(&self, writer: &mut std::fmt::Write) {");
self.indent(); self.indent();
self.handle(nodes); self.handle(nodes);
self.flush_ws(&WS(false, false));
self.dedent(); self.dedent();
self.writeln("}"); self.writeln("}");
@ -304,6 +346,7 @@ pub fn generate(ast: &syn::DeriveInput, path: &str, mut nodes: Vec<Node>) -> Str
} }
} }
let empty = Vec::new();
let mut gen = Generator::new(); let mut gen = Generator::new();
gen.path_based_name(ast, path); gen.path_based_name(ast, path);
if !blocks.is_empty() { if !blocks.is_empty() {
@ -313,7 +356,7 @@ pub fn generate(ast: &syn::DeriveInput, path: &str, mut nodes: Vec<Node>) -> Str
} }
} else { } else {
gen.template_trait(ast, path, &blocks, &content); gen.template_trait(ast, path, &blocks, &content);
gen.trait_impl(ast, path, &Vec::new()); gen.trait_impl(ast, path, &empty);
} }
gen.trait_based_impl(ast); gen.trait_based_impl(ast);
} else { } else {

View File

@ -13,7 +13,7 @@ pub enum Target<'a> {
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct WS(bool, bool); pub struct WS(pub bool, pub bool);
pub enum Node<'a> { pub enum Node<'a> {
Lit(&'a str, &'a str, &'a str), Lit(&'a str, &'a str, &'a str),

View File

@ -1,6 +1,48 @@
{% if a == b %}t{% endif %}{% if a == c %}t{% else %}f{% endif %} {% if a == b -%}
{% if a != c %}t{% endif %}{% if a != b %}t{% else %}f{% endif %} t
{% if c >= b %}t{% endif %}{% if b >= c %}t{% else %}f{% endif %} {%- endif -%}
{% if c > b %}t{% endif %}{% if a > c %}t{% else %}f{% endif %} {% if a == c -%}
{% if a <= b %}t{% endif %}{% if c <= b %}t{% else %}f{% endif %} t
{% if a < c %}t{% endif %}{% if a < b %}t{% else %}f{% endif %} {%- else -%}
f
{%- endif %}
{% if a != c -%}
t
{%- endif %}
{%- if a != b -%}
t
{%- else -%}
f
{%- endif %}
{% if c >= b -%}
t
{%- endif -%}
{% if b >= c -%}
t
{%- else -%}
f
{%- endif %}
{% if c > b -%}
t
{%- endif -%}
{% if a > c -%}
t
{%- else -%}
f
{%- endif %}
{% if a <= b %}
t
{% endif -%}
{% if c <= b -%}
t
{%- else -%}
f
{%- endif %}
{% if a < c -%}
t
{%- endif %}
{%- if a < b -%}
t
{%- else -%}
f
{%- endif %}