diff options
Diffstat (limited to 'askama_derive')
| -rw-r--r-- | askama_derive/src/generator.rs | 6 | ||||
| -rw-r--r-- | askama_derive/src/input.rs | 46 | ||||
| -rw-r--r-- | askama_derive/src/lib.rs | 13 | ||||
| -rw-r--r-- | askama_derive/src/parser.rs | 174 | 
4 files changed, 160 insertions, 79 deletions
| diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs index 2a233d5..33fbe4c 100644 --- a/askama_derive/src/generator.rs +++ b/askama_derive/src/generator.rs @@ -1,6 +1,6 @@  use super::{get_template_source, Context, Heritage};  use input::TemplateInput; -use parser::{self, Cond, Expr, MatchParameter, MatchVariant, Node, Target, When, WS}; +use parser::{Cond, Expr, MatchParameter, MatchVariant, Node, Target, When, WS};  use shared::filters;  use proc_macro2::Span; @@ -13,6 +13,8 @@ use std::{cmp, hash, str};  use syn; +use parser::parse; +  pub(crate) fn generate(      input: &TemplateInput,      contexts: &HashMap<&PathBuf, Context>, @@ -482,7 +484,7 @@ impl<'a> Generator<'a> {              .config              .find_template(path, Some(&self.input.path));          let src = get_template_source(&path); -        let nodes = parser::parse(&src); +        let nodes = parse(&src, self.input.syntax);          {              // Since nodes must not outlive the Generator, we instantiate              // a nested Generator here to handle the include's nodes. diff --git a/askama_derive/src/input.rs b/askama_derive/src/input.rs index 14531cf..5d3fa76 100644 --- a/askama_derive/src/input.rs +++ b/askama_derive/src/input.rs @@ -2,7 +2,7 @@ use proc_macro2::TokenStream;  use quote::ToTokens; -use shared::Config; +use shared::{Config, Syntax};  use std::path::PathBuf; @@ -10,7 +10,8 @@ use syn;  pub struct TemplateInput<'a> {      pub ast: &'a syn::DeriveInput, -    pub config: Config, +    pub config: &'a Config<'a>, +    pub syntax: &'a Syntax<'a>,      pub source: Source,      pub print: Print,      pub escaping: EscapeMode, @@ -24,7 +25,7 @@ impl<'a> TemplateInput<'a> {      /// mostly recovers the data for the `TemplateInput` fields from the      /// `template()` attribute list fields; it also finds the of the `_parent`      /// field, if any. -    pub fn new(ast: &'a syn::DeriveInput) -> TemplateInput<'a> { +    pub fn new<'n>(ast: &'n syn::DeriveInput, config: &'n Config) -> TemplateInput<'n> {          // Check that an attribute called `template()` exists and that it is          // the proper type (list).          let mut meta = None; @@ -53,6 +54,7 @@ impl<'a> TemplateInput<'a> {          let mut print = Print::None;          let mut escaping = None;          let mut ext = None; +        let mut syntax = None;          for nm_item in meta_list.nested {              if let syn::NestedMeta::Meta(ref item) = nm_item {                  if let syn::Meta::NameValue(ref pair) = item { @@ -88,6 +90,11 @@ impl<'a> TemplateInput<'a> {                          } else {                              panic!("ext value must be string literal");                          }, +                        "syntax" => if let syn::Lit::Str(ref s) = pair.lit { +                            syntax = Some(s.value()) +                        } else { +                            panic!("syntax value must be string literal"); +                        },                          attr => panic!("unsupported annotation key '{}' found", attr),                      }                  } @@ -97,7 +104,6 @@ impl<'a> TemplateInput<'a> {          // Validate the `source` and `ext` value together, since they are          // related. In case `source` was used instead of `path`, the value          // of `ext` is merged into a synthetic `path` value here. -        let config = Config::new();          let source = source.expect("template path or source not found in attributes");          let path = match (&source, &ext) {              (&Source::Path(ref path), None) => config.find_template(path, None), @@ -124,6 +130,37 @@ impl<'a> TemplateInput<'a> {              _ => None,          }; +        // Validate syntax +        let syntax = syntax.map_or_else( +            || config.syntaxes.get(config.default_syntax).unwrap(), +            |s| { +                config +                    .syntaxes +                    .get(&s) +                    .expect(&format!("attribute syntax {} not exist", s)) +            }, +        ); + +        if syntax.block_start.len() != 2 +            || syntax.block_end.len() != 2 +            || syntax.expr_start.len() != 2 +            || syntax.expr_end.len() != 2 +            || syntax.comment_start.len() != 2 +            || syntax.comment_end.len() != 2 +        { +            panic!("length of delimiters must be two") +        } + +        let bs = syntax.block_start.as_bytes()[0]; +        let be = syntax.block_start.as_bytes()[1]; +        let cs = syntax.comment_start.as_bytes()[0]; +        let ce = syntax.comment_start.as_bytes()[1]; +        let es = syntax.block_start.as_bytes()[0]; +        let ee = syntax.block_start.as_bytes()[1]; +        if !(bs == cs && bs == es) && !(be == ce && be == ee) { +            panic!("bad delimiters block_start: {}, comment_start: {}, expr_start: {}, needs one of the two characters in common", syntax.block_start, syntax.comment_start, syntax.expr_start); +        } +          TemplateInput {              ast,              config, @@ -133,6 +170,7 @@ impl<'a> TemplateInput<'a> {              ext,              parent,              path, +            syntax,          }      }  } diff --git a/askama_derive/src/lib.rs b/askama_derive/src/lib.rs index 45d98b9..cb7793f 100644 --- a/askama_derive/src/lib.rs +++ b/askama_derive/src/lib.rs @@ -14,8 +14,9 @@ mod parser;  use input::{Print, Source, TemplateInput};  use parser::{Expr, Macro, Node};  use proc_macro::TokenStream; -use shared::Config; +use shared::{read_config_file, Config}; +use parser::parse;  use std::collections::HashMap;  use std::fs;  use std::path::{Path, PathBuf}; @@ -34,7 +35,9 @@ pub fn derive_template(input: TokenStream) -> TokenStream {  /// the parse tree and/or generated source according to the `print` key's  /// value as passed to the `template()` attribute.  fn build_template(ast: &syn::DeriveInput) -> String { -    let input = TemplateInput::new(ast); +    let file = read_config_file(); +    let config = Config::new(&file); +    let input = TemplateInput::new(ast, &config);      let source: String = match input.source {          Source::Source(ref s) => s.clone(),          Source::Path(_) => get_template_source(&input.path), @@ -45,7 +48,7 @@ fn build_template(ast: &syn::DeriveInput) -> String {      let mut parsed = HashMap::new();      for (path, src) in &sources { -        parsed.insert(path, parser::parse(src)); +        parsed.insert(path, parse(src, input.syntax));      }      let mut contexts = HashMap::new(); @@ -74,7 +77,7 @@ fn build_template(ast: &syn::DeriveInput) -> String {  fn find_used_templates(input: &TemplateInput, map: &mut HashMap<PathBuf, String>, source: String) {      let mut check = vec![(input.path.clone(), source)];      while let Some((path, source)) = check.pop() { -        for n in parser::parse(&source) { +        for n in parse(&source, input.syntax) {              match n {                  Node::Extends(Expr::StrLit(extends)) => {                      let extends = input.config.find_template(extends, Some(&path)); @@ -220,7 +223,7 @@ mod tests {      #[test]      fn get_source() { -        let path = Config::new().find_template("b.html", None); +        let path = Config::new("").find_template("b.html", None);          assert_eq!(get_template_source(&path), "bar");      }  } diff --git a/askama_derive/src/parser.rs b/askama_derive/src/parser.rs index 184dc8d..d5dd5dc 100644 --- a/askama_derive/src/parser.rs +++ b/askama_derive/src/parser.rs @@ -4,6 +4,8 @@  use nom;  use std::str; +use shared::Syntax; +  #[derive(Debug)]  pub enum Expr<'a> {      NumLit(&'a str), @@ -111,18 +113,33 @@ enum ContentState {      End(usize),  } -fn take_content(i: Input) -> Result<(Input, Node), nom::Err<Input>> { +fn take_content<'a>(i: Input<'a>, s: &'a Syntax<'a>) -> Result<(Input<'a>, Node<'a>), nom::Err<Input<'a>>>{      use parser::ContentState::*; +    let bs = s.block_start.as_bytes()[0]; +    let be = s.block_start.as_bytes()[1]; +    let cs = s.comment_start.as_bytes()[0]; +    let ce = s.comment_start.as_bytes()[1]; +    let es = s.expr_start.as_bytes()[0]; +    let ee = s.expr_start.as_bytes()[1]; +      let mut state = Any;      for (idx, c) in i.iter().enumerate() { -        state = match (state, *c) { -            (Any, b'{') => Brace(idx), -            (Brace(start), b'{') | -            (Brace(start), b'%') | -            (Brace(start), b'#') => End(start), -            (Any, _) | -            (Brace(_), _) => Any, -            (End(_), _) => panic!("cannot happen"), +        state = match state { +            Any => { +                if *c == bs || *c == es || *c == cs { +                    Brace(idx) +                } else { +                    Any +                } +            } +            Brace(start) => { +                if *c == be || *c == ee || *c == ce { +                    End(start) +                } else { +                    Any +                } +            } +            End(_) => panic!("cannot happen"),          };          if let End(_) = state {              break; @@ -444,12 +461,12 @@ named!(expr_any<Input, Expr>, alt!(      expr_or  )); -named!(expr_node<Input, Node>, do_parse!( -    tag_s!("{{") >> +named_args!(expr_node<'a>(s: &'a Syntax<'a>) <Input<'a>, Node<'a>>, do_parse!( +    call!(tag_expr_start, s) >>      pws: opt!(tag_s!("-")) >>      expr: ws!(expr_any) >>      nws: opt!(tag_s!("-")) >> -    tag_s!("}}") >> +    call!(tag_expr_end, s) >>      (Node::Expr(WS(pws.is_some(), nws.is_some()), expr))  )); @@ -473,25 +490,25 @@ named!(cond_if<Input, Expr>, do_parse!(      (cond)  )); -named!(cond_block<Input, Cond>, do_parse!( -    tag_s!("{%") >> +named_args!(cond_block<'a>(s: &'a Syntax<'a>) <Input<'a>, Cond<'a>>, do_parse!( +    call!(tag_block_start, s) >>      pws: opt!(tag_s!("-")) >>      ws!(tag_s!("else")) >>      cond: opt!(cond_if) >>      nws: opt!(tag_s!("-")) >> -    tag_s!("%}") >> -    block: parse_template >> +    call!(tag_block_end, s) >> +    block: call!(parse_template, s) >>      (WS(pws.is_some(), nws.is_some()), cond, block)  )); -named!(block_if<Input, Node>, do_parse!( +named_args!(block_if<'a>(s: &'a Syntax<'a>) <Input<'a>, Node<'a>>, do_parse!(      pws1: opt!(tag_s!("-")) >>      cond: ws!(cond_if) >>      nws1: opt!(tag_s!("-")) >> -    tag_s!("%}") >> -    block: parse_template >> -    elifs: many0!(cond_block) >> -    tag_s!("{%") >> +    call!(tag_block_end, s) >> +    block: call!(parse_template, s) >> +    elifs: many0!(call!(cond_block, s)) >> +    call!(tag_block_start, s) >>      pws2: opt!(tag_s!("-")) >>      ws!(tag_s!("endif")) >>      nws2: opt!(tag_s!("-")) >> @@ -503,38 +520,38 @@ named!(block_if<Input, Node>, do_parse!(      })  )); -named!(match_else_block<Input, When>, do_parse!( -    tag_s!("{%") >> +named_args!(match_else_block<'a>(s: &'a Syntax<'a>) <Input<'a>, When<'a>>, do_parse!( +    call!(tag_block_start, s) >>      pws: opt!(tag_s!("-")) >>      ws!(tag_s!("else")) >>      nws: opt!(tag_s!("-")) >> -    tag_s!("%}") >> -    block: parse_template >> +    call!(tag_block_end, s) >> +    block: call!(parse_template, s) >>      (WS(pws.is_some(), nws.is_some()), None, vec![], block)  )); -named!(when_block<Input, When>, do_parse!( -    tag_s!("{%") >> +named_args!(when_block<'a>(s: &'a Syntax<'a>) <Input<'a>, When<'a>>, do_parse!( +    call!(tag_block_start, s) >>      pws: opt!(tag_s!("-")) >>      ws!(tag_s!("when")) >>      variant: ws!(match_variant) >>      params: opt!(ws!(with_parameters)) >>      nws: opt!(tag_s!("-")) >> -    tag_s!("%}") >> -    block: parse_template >> +    call!(tag_block_end, s) >> +    block: call!(parse_template, s) >>      (WS(pws.is_some(), nws.is_some()), Some(variant), params.unwrap_or_default(), block)  )); -named!(block_match<Input, Node>, do_parse!( +named_args!(block_match<'a>(s: &'a Syntax<'a>) <Input<'a>, Node<'a>>, do_parse!(      pws1: opt!(tag_s!("-")) >>      ws!(tag_s!("match")) >>      expr: ws!(expr_any) >>      nws1: opt!(tag_s!("-")) >> -    tag_s!("%}") >> -    inter: opt!(take_content) >> -    arms: many1!(when_block) >> -    else_arm: opt!(match_else_block) >> -    ws!(tag_s!("{%")) >> +    call!(tag_block_end, s) >> +    inter: opt!(call!(take_content, s)) >> +    arms: many1!(call!(when_block, s)) >> +    else_arm: opt!(call!(match_else_block, s)) >> +    ws!(call!(tag_block_start, s)) >>      pws2: opt!(tag_s!("-")) >>      ws!(tag_s!("endmatch")) >>      nws2: opt!(tag_s!("-")) >> @@ -581,16 +598,16 @@ named!(block_let<Input, Node>, do_parse!(      })  )); -named!(block_for<Input, Node>, do_parse!( +named_args!(block_for<'a>(s: &'a Syntax<'a>) <Input<'a>, Node<'a>>, do_parse!(      pws1: opt!(tag_s!("-")) >>      ws!(tag_s!("for")) >>      var: ws!(target_single) >>      ws!(tag_s!("in")) >>      iter: ws!(expr_any) >>      nws1: opt!(tag_s!("-")) >> -    tag_s!("%}") >> -    block: parse_template >> -    tag_s!("{%") >> +    call!(tag_block_end, s) >> +    block: call!(parse_template, s) >> +    call!(tag_block_start, s) >>      pws2: opt!(tag_s!("-")) >>      ws!(tag_s!("endfor")) >>      nws2: opt!(tag_s!("-")) >> @@ -605,14 +622,14 @@ named!(block_extends<Input, Node>, do_parse!(      (Node::Extends(name))  )); -named!(block_block<Input, Node>, do_parse!( +named_args!(block_block<'a>(s: &'a Syntax<'a>) <Input<'a>, Node<'a>>, do_parse!(      pws1: opt!(tag_s!("-")) >>      ws!(tag_s!("block")) >>      name: ws!(identifier) >>      nws1: opt!(tag_s!("-")) >> -    tag_s!("%}") >> -    contents: parse_template >> -    tag_s!("{%") >> +    call!(tag_block_end, s) >> +    contents: call!(parse_template, s) >> +    call!(tag_block_start, s) >>      pws2: opt!(tag_s!("-")) >>      ws!(tag_s!("endblock")) >>      opt!(ws!(tag_s!(name))) >> @@ -646,15 +663,15 @@ named!(block_import<Input, Node>, do_parse!(      }, scope))  )); -named!(block_macro<Input, Node>, do_parse!( +named_args!(block_macro<'a>(s: &'a Syntax<'a>) <Input<'a>, Node<'a>>, do_parse!(      pws1: opt!(tag_s!("-")) >>      ws!(tag_s!("macro")) >>      name: ws!(identifier) >>      params: ws!(parameters) >>      nws1: opt!(tag_s!("-")) >> -    tag_s!("%}") >> -    contents: parse_template >> -    tag_s!("{%") >> +    call!(tag_block_end, s) >> +    contents: call!(parse_template, s) >> +    call!(tag_block_start, s) >>      pws2: opt!(tag_s!("-")) >>      ws!(tag_s!("endmacro")) >>      nws2: opt!(tag_s!("-")) >> @@ -674,41 +691,48 @@ named!(block_macro<Input, Node>, do_parse!(      })  )); -named!(block_node<Input, Node>, do_parse!( -    tag_s!("{%") >> +named_args!(block_node<'a>(s: &'a Syntax<'a>) <Input<'a>, Node<'a>>, do_parse!( +    call!(tag_block_start, s) >>      contents: alt!(          block_call |          block_let | -        block_if | -        block_for | -        block_match | +        call!(block_if, s) | +        call!(block_for, s) | +        call!(block_match, s) |          block_extends |          block_include |          block_import | -        block_block | -        block_macro +        call!(block_block, s) | +        call!(block_macro, s)      ) >> -    tag_s!("%}") >> +    call!(tag_block_end, s) >>      (contents)  )); -named!(block_comment<Input, Node>, do_parse!( -    tag_s!("{#") >> +named_args!(block_comment<'a>(s: &'a Syntax<'a>) <Input<'a>, Node<'a>>, do_parse!( +    call!(tag_comment_start, s)  >>      pws: opt!(tag_s!("-")) >> -    inner: take_until_s!("#}") >> -    tag_s!("#}") >> +    inner: take_until_s!(s.comment_end) >> +    call!(tag_comment_end, s) >>      (Node::Comment(WS(pws.is_some(), inner.len() > 1 && inner[inner.len() - 1] == b'-')))  )); -named!(parse_template<Input, Vec<Node>>, many0!(alt!( -    take_content | -    block_comment | -    expr_node | -    block_node +named_args!(parse_template<'a>(s: &'a Syntax<'a>)<Input<'a>, Vec<Node<'a>>>, many0!(alt!( +    call!(take_content, s) | +    call!(block_comment, s) | +    call!(expr_node, s) | +    call!(block_node, s)  ))); -pub fn parse(src: &str) -> Vec<Node> { -    match parse_template(Input(src.as_bytes())) { +named_args!(tag_block_start<'a>(s: &'a Syntax<'a>) <Input<'a>, Input<'a>>, tag!(s.block_start)); +named_args!(tag_block_end<'a>(s: &'a Syntax<'a>) <Input<'a>, Input<'a>>, tag!(s.block_end)); +named_args!(tag_comment_start<'a>(s: &'a Syntax<'a>) <Input<'a>, Input<'a>>, tag!(s.comment_start)); +named_args!(tag_comment_end<'a>(s: &'a Syntax<'a>) <Input<'a>, Input<'a>>, tag!(s.comment_end)); +named_args!(tag_expr_start<'a>(s: &'a Syntax<'a>) <Input<'a>, Input<'a>>, tag!(s.expr_start)); +named_args!(tag_expr_end<'a>(s: &'a Syntax<'a>) <Input<'a>, Input<'a>>, tag!(s.expr_end)); + +pub fn parse<'a>(src: &'a str, syntax: &'a Syntax<'a>) -> Vec<Node<'a>> { +    match parse_template(Input(src.as_bytes()), syntax) {          Ok((left, res)) => {              if !left.is_empty() {                  let s = str::from_utf8(left.0).unwrap(); @@ -725,6 +749,8 @@ pub fn parse(src: &str) -> Vec<Node> {  #[cfg(test)]  mod tests { +    use shared::Syntax; +      fn check_ws_split(s: &str, res: &(&str, &str, &str)) {          let node = super::split_ws_parts(s.as_bytes());          match node { @@ -747,11 +773,23 @@ mod tests {      #[test]      #[should_panic]      fn test_invalid_block() { -        super::parse("{% extend \"blah\" %}"); +        super::parse("{% extend \"blah\" %}", &Syntax::default());      }      #[test]      fn test_parse_filter() { -        super::parse("{{ strvar|e }}"); +        super::parse("{{ strvar|e }}", &Syntax::default());      } + +    #[test] +    fn change_delimiters_parse_filter() { +        let syntax = Syntax { +            expr_start: "{~", +            expr_end: "~}", +            ..Syntax::default() +        }; + +        super::parse("{~ strvar|e ~}", &syntax); +    } +  } | 
