From 1a9a82ac7db88de4ea795f6e7b110574d618f582 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Wed, 9 Aug 2017 21:44:24 +0200 Subject: Keep better track of nested scope through scope chains --- askama_derive/src/generator.rs | 67 +++++++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 17 deletions(-) (limited to 'askama_derive/src') diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs index 409b698..e9a4058 100644 --- a/askama_derive/src/generator.rs +++ b/askama_derive/src/generator.rs @@ -3,8 +3,8 @@ use path; use quote::{Tokens, ToTokens}; +use std::{cmp, hash, str}; use std::path::PathBuf; -use std::str; use std::collections::HashSet; use syn; @@ -31,8 +31,7 @@ pub fn generate(ast: &syn::DeriveInput, path: &str, mut nodes: Vec) -> Str } } - let mut locals = HashSet::new(); - let mut gen = Generator::default(&mut locals); + let mut gen = Generator::default(); if !blocks.is_empty() { let trait_name = trait_name_for_path(&base, path); if base.is_none() { @@ -98,14 +97,14 @@ struct Generator<'a> { buf: String, indent: u8, start: bool, - locals: &'a mut HashSet, + locals: SetChain<'a, &'a str>, next_ws: Option<&'a str>, skip_ws: bool, } impl<'a> Generator<'a> { - fn new<'n>(locals: &'n mut HashSet, indent: u8) -> Generator<'n> { + fn new<'n>(locals: SetChain<'n, &'n str>, indent: u8) -> Generator<'n> { Generator { buf: String::new(), indent: indent, @@ -116,12 +115,13 @@ impl<'a> Generator<'a> { } } - fn default<'n>(locals: &'n mut HashSet) -> Generator<'n> { - Self::new(locals, 0) + fn default<'n>() -> Generator<'n> { + Self::new(SetChain::new(), 0) } fn child<'n>(&'n mut self) -> Generator<'n> { - Self::new(self.locals, self.indent) + let locals = SetChain::with_parent(&self.locals); + Self::new(locals, self.indent) } /* Helper methods for writing to internal buffer */ @@ -184,15 +184,19 @@ impl<'a> Generator<'a> { /* Helper methods for dealing with scope */ fn is_local(&self, var: &str) -> bool { - self.locals.contains(var) + self.locals.contains(&var) } - fn make_local(&mut self, var: &str) { - self.locals.insert(var.to_string()); + fn make_local<'s>(&mut self, var: &'a str) { + self.locals.insert(var); } - fn drop_local(&mut self, var: &str) { - self.locals.remove(var); + fn push_scope(&mut self) { + self.locals.push(); + } + + fn pop_scope(&mut self) { + self.locals.pop(); } /* Visitor methods for expression types */ @@ -351,9 +355,10 @@ impl<'a> Generator<'a> { self.writeln("}"); } - fn write_loop(&mut self, ws1: &WS, var: &Target, iter: &Expr, + fn write_loop(&mut self, ws1: &WS, var: &'a Target, iter: &Expr, body: &'a [Node], ws2: &WS) { self.handle_ws(ws1); + self.push_scope(); self.write("for (_loop_index, "); let targets = self.visit_target(var); for name in &targets { @@ -367,9 +372,7 @@ impl<'a> Generator<'a> { self.handle(body); self.handle_ws(ws2); self.writeln("}"); - for name in &targets { - self.drop_local(name); - } + self.pop_scope(); } fn write_block(&mut self, ws1: &WS, name: &str, ws2: &WS) { @@ -576,3 +579,33 @@ impl<'a> Generator<'a> { } } + +struct SetChain<'a, T: 'a> where T: cmp::Eq + hash::Hash { + parent: Option<&'a SetChain<'a, T>>, + scopes: Vec>, +} + +impl<'a, T: 'a> SetChain<'a, T> where T: cmp::Eq + hash::Hash { + fn new() -> SetChain<'a, T> { + SetChain { parent: None, scopes: vec![HashSet::new()] } + } + fn with_parent<'p>(parent: &'p SetChain) -> SetChain<'p, T> { + SetChain { parent: Some(parent), scopes: vec![HashSet::new()] } + } + fn contains(&self, val: &T) -> bool { + self.scopes.iter().rev().any(|set| set.contains(val)) || + match self.parent { + Some(set) => set.contains(val), + None => false, + } + } + fn insert(&mut self, val: T) { + self.scopes.last_mut().unwrap().insert(val); + } + fn push(&mut self) { + self.scopes.push(HashSet::new()); + } + fn pop(&mut self) { + self.scopes.pop().unwrap(); + } +} -- cgit