aboutsummaryrefslogtreecommitdiffstats
path: root/askama_derive
diff options
context:
space:
mode:
authorLibravatar bott <mhpoin@gmail.com>2018-10-05 05:51:22 +0200
committerLibravatar Dirkjan Ochtman <dirkjan@ochtman.nl>2018-10-05 15:55:14 +0200
commit4bd302c68817fc78ff31594b1175a3b67cece171 (patch)
treef85361f6dc3d3045576aaae9efaacd0daed2a228 /askama_derive
parentaf880b3f202d3b94a7a38ec9a8503d3a18d924e6 (diff)
downloadaskama-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.rs6
-rw-r--r--askama_derive/src/input.rs46
-rw-r--r--askama_derive/src/lib.rs13
-rw-r--r--askama_derive/src/parser.rs174
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);
+ }
+
}