diff options
author | bott <mhpoin@gmail.com> | 2018-10-05 05:51:22 +0200 |
---|---|---|
committer | Dirkjan Ochtman <dirkjan@ochtman.nl> | 2018-10-05 15:55:14 +0200 |
commit | 4bd302c68817fc78ff31594b1175a3b67cece171 (patch) | |
tree | f85361f6dc3d3045576aaae9efaacd0daed2a228 /askama_derive | |
parent | af880b3f202d3b94a7a38ec9a8503d3a18d924e6 (diff) | |
download | askama-4bd302c68817fc78ff31594b1175a3b67cece171.tar.gz askama-4bd302c68817fc78ff31594b1175a3b67cece171.tar.bz2 askama-4bd302c68817fc78ff31594b1175a3b67cece171.zip |
Add changing delimiters support
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); + } + } |