1 use std
::collections
::HashMap
;
3 use lazy_static
::lazy_static
;
4 use pest
::iterators
::Pair
;
5 use pest
::prec_climber
::{Assoc, Operator, PrecClimber}
;
7 use pest_derive
::Parser
;
9 use crate::errors
::{Error, Result as TeraResult}
;
11 // This include forces recompiling this source file if the grammar file changes.
12 // Uncomment it when doing changes to the .pest file
13 const _GRAMMAR
: &str = include_str
!("tera.pest");
16 #[grammar = "parser/tera.pest"]
17 pub struct TeraParser
;
27 pub use self::whitespace
::remove_whitespace
;
30 static ref MATH_CLIMBER
: PrecClimber
<Rule
> = PrecClimber
::new(vec
![
32 Operator
::new(Rule
::op_plus
, Assoc
::Left
) | Operator
::new(Rule
::op_minus
, Assoc
::Left
),
34 Operator
::new(Rule
::op_times
, Assoc
::Left
) |
35 Operator
::new(Rule
::op_slash
, Assoc
::Left
) |
36 Operator
::new(Rule
::op_modulo
, Assoc
::Left
),
38 static ref COMPARISON_EXPR_CLIMBER
: PrecClimber
<Rule
> = PrecClimber
::new(vec
![
39 // <, <=, >, >=, ==, !=
40 Operator
::new(Rule
::op_lt
, Assoc
::Left
) | Operator
::new(Rule
::op_lte
, Assoc
::Left
)
41 | Operator
::new(Rule
::op_gt
, Assoc
::Left
) | Operator
::new(Rule
::op_gte
, Assoc
::Left
)
42 | Operator
::new(Rule
::op_eq
, Assoc
::Left
) | Operator
::new(Rule
::op_ineq
, Assoc
::Left
),
44 static ref LOGIC_EXPR_CLIMBER
: PrecClimber
<Rule
> = PrecClimber
::new(vec
![
45 Operator
::new(Rule
::op_or
, Assoc
::Left
),
46 Operator
::new(Rule
::op_and
, Assoc
::Left
),
50 /// Strings are delimited by double quotes, single quotes and backticks
51 /// We need to remove those before putting them in the AST
52 fn replace_string_markers(input
: &str) -> String
{
53 match input
.chars().next().unwrap() {
54 '
"' => input.replace('"'
, ""),
55 '
\''
=> input
.replace('
\''
, ""),
56 '`'
=> input
.replace('`'
, ""),
57 _
=> unreachable
!("How did you even get there"),
61 fn parse_kwarg(pair
: Pair
<Rule
>) -> TeraResult
<(String
, Expr
)> {
65 for p
in pair
.into_inner() {
67 Rule
::ident
=> name
= Some(p
.as_span().as_str().to_string()),
68 Rule
::logic_expr
=> val
= Some(parse_logic_expr(p
)?
),
69 Rule
::array_filter
=> val
= Some(parse_array_with_filters(p
)?
),
70 _
=> unreachable
!("{:?} not supposed to get there (parse_kwarg)!", p
.as_rule()),
74 Ok((name
.unwrap(), val
.unwrap()))
77 fn parse_fn_call(pair
: Pair
<Rule
>) -> TeraResult
<FunctionCall
> {
79 let mut args
= HashMap
::new();
81 for p
in pair
.into_inner() {
83 Rule
::ident
=> name
= Some(p
.as_span().as_str().to_string()),
85 let (name
, val
) = parse_kwarg(p
)?
;
86 args
.insert(name
, val
);
88 _
=> unreachable
!("{:?} not supposed to get there (parse_fn_call)!", p
.as_rule()),
92 Ok(FunctionCall { name: name.unwrap(), args }
)
95 fn parse_filter(pair
: Pair
<Rule
>) -> TeraResult
<FunctionCall
> {
97 let mut args
= HashMap
::new();
98 for p
in pair
.into_inner() {
100 Rule
::ident
=> name
= Some(p
.as_span().as_str().to_string()),
102 let (name
, val
) = parse_kwarg(p
)?
;
103 args
.insert(name
, val
);
106 return parse_fn_call(p
);
108 _
=> unreachable
!("{:?} not supposed to get there (parse_filter)!", p
.as_rule()),
112 Ok(FunctionCall { name: name.unwrap(), args }
)
115 fn parse_test_call(pair
: Pair
<Rule
>) -> TeraResult
<(String
, Vec
<Expr
>)> {
117 let mut args
= vec
![];
119 for p
in pair
.into_inner() {
121 Rule
::ident
=> name
= Some(p
.as_span().as_str().to_string()),
123 // iterate on the test_arg rule
125 for p2
in p
.into_inner() {
127 Rule
::logic_expr
=> {
128 args
.push(parse_logic_expr(p2
)?
);
131 args
.push(Expr
::new(parse_array(p2
)?
));
133 _
=> unreachable
!("Invalid arg type for test {:?}", p2
.as_rule()),
137 _
=> unreachable
!("{:?} not supposed to get there (parse_test_call)!", p
.as_rule()),
141 Ok((name
.unwrap(), args
))
144 fn parse_test(pair
: Pair
<Rule
>) -> TeraResult
<Test
> {
145 let mut ident
= None
;
147 let mut args
= vec
![];
149 for p
in pair
.into_inner() {
151 Rule
::dotted_ident
=> ident
= Some(p
.as_str().to_string()),
153 let (_name
, _args
) = parse_test_call(p
)?
;
157 _
=> unreachable
!("{:?} not supposed to get there (parse_ident)!", p
.as_rule()),
161 Ok(Test { ident: ident.unwrap(), negated: false, name: name.unwrap(), args }
)
164 fn parse_string_concat(pair
: Pair
<Rule
>) -> TeraResult
<ExprVal
> {
165 let mut values
= vec
![];
166 let mut current_str
= String
::new();
168 // Can we fold it into a simple string?
169 for p
in pair
.into_inner() {
172 current_str
.push_str(&replace_string_markers(p
.as_str()));
175 if !current_str
.is_empty() {
176 values
.push(ExprVal
::String(current_str
));
177 current_str
= String
::new();
179 values
.push(ExprVal
::Int(p
.as_str().parse().map_err(|_
| {
180 Error
::msg(format
!("Integer out of bounds: `{}`", p
.as_str()))
184 if !current_str
.is_empty() {
185 values
.push(ExprVal
::String(current_str
));
186 current_str
= String
::new();
188 values
.push(ExprVal
::Float(
189 p
.as_str().parse().map_err(|_
| {
190 Error
::msg(format
!("Float out of bounds: `{}`", p
.as_str()))
194 Rule
::dotted_square_bracket_ident
=> {
195 if !current_str
.is_empty() {
196 values
.push(ExprVal
::String(current_str
));
197 current_str
= String
::new();
199 values
.push(ExprVal
::Ident(p
.as_str().to_string()))
202 if !current_str
.is_empty() {
203 values
.push(ExprVal
::String(current_str
));
204 current_str
= String
::new();
206 values
.push(ExprVal
::FunctionCall(parse_fn_call(p
)?
))
208 _
=> unreachable
!("Got {:?} in parse_string_concat", p
),
212 if values
.is_empty() {
213 // we only got a string
214 return Ok(ExprVal
::String(current_str
));
217 if !current_str
.is_empty() {
218 values
.push(ExprVal
::String(current_str
));
221 Ok(ExprVal
::StringConcat(StringConcat { values }
))
224 fn parse_basic_expression(pair
: Pair
<Rule
>) -> TeraResult
<ExprVal
> {
225 let primary
= |pair
| parse_basic_expression(pair
);
227 let infix
= |lhs
: TeraResult
<ExprVal
>, op
: Pair
<Rule
>, rhs
: TeraResult
<ExprVal
>| {
228 Ok(ExprVal
::Math(MathExpr
{
229 lhs
: Box
::new(Expr
::new(lhs?
)),
230 operator
: match op
.as_rule() {
231 Rule
::op_plus
=> MathOperator
::Add
,
232 Rule
::op_minus
=> MathOperator
::Sub
,
233 Rule
::op_times
=> MathOperator
::Mul
,
234 Rule
::op_slash
=> MathOperator
::Div
,
235 Rule
::op_modulo
=> MathOperator
::Modulo
,
238 rhs
: Box
::new(Expr
::new(rhs?
)),
242 let expr
= match pair
.as_rule() {
243 Rule
::int
=> ExprVal
::Int(
246 .map_err(|_
| Error
::msg(format
!("Integer out of bounds: `{}`", pair
.as_str())))?
,
248 Rule
::float
=> ExprVal
::Float(
251 .map_err(|_
| Error
::msg(format
!("Float out of bounds: `{}`", pair
.as_str())))?
,
253 Rule
::boolean
=> match pair
.as_str() {
254 "true" => ExprVal
::Bool(true),
255 "True" => ExprVal
::Bool(true),
256 "false" => ExprVal
::Bool(false),
257 "False" => ExprVal
::Bool(false),
260 Rule
::test
=> ExprVal
::Test(parse_test(pair
)?
),
262 let mut test
= parse_test(pair
)?
;
266 Rule
::fn_call
=> ExprVal
::FunctionCall(parse_fn_call(pair
)?
),
267 Rule
::macro_call
=> ExprVal
::MacroCall(parse_macro_call(pair
)?
),
268 Rule
::dotted_square_bracket_ident
=> ExprVal
::Ident(pair
.as_str().to_string()),
269 Rule
::basic_expr
=> MATH_CLIMBER
.climb(pair
.into_inner(), primary
, infix
)?
,
270 _
=> unreachable
!("Got {:?} in parse_basic_expression: {}", pair
.as_rule(), pair
.as_str()),
275 /// A basic expression with optional filters
276 fn parse_basic_expr_with_filters(pair
: Pair
<Rule
>) -> TeraResult
<Expr
> {
277 let mut expr_val
= None
;
278 let mut filters
= vec
![];
280 for p
in pair
.into_inner() {
282 Rule
::basic_expr
=> expr_val
= Some(parse_basic_expression(p
)?
),
283 Rule
::filter
=> filters
.push(parse_filter(p
)?
),
284 _
=> unreachable
!("Got {:?}", p
),
288 Ok(Expr { val: expr_val.unwrap(), negated: false, filters }
)
291 /// A string expression with optional filters
292 fn parse_string_expr_with_filters(pair
: Pair
<Rule
>) -> TeraResult
<Expr
> {
293 let mut expr_val
= None
;
294 let mut filters
= vec
![];
296 for p
in pair
.into_inner() {
298 Rule
::string
=> expr_val
= Some(ExprVal
::String(replace_string_markers(p
.as_str()))),
299 Rule
::string_concat
=> expr_val
= Some(parse_string_concat(p
)?
),
300 Rule
::filter
=> filters
.push(parse_filter(p
)?
),
301 _
=> unreachable
!("Got {:?}", p
),
305 Ok(Expr { val: expr_val.unwrap(), negated: false, filters }
)
308 /// An array with optional filters
309 fn parse_array_with_filters(pair
: Pair
<Rule
>) -> TeraResult
<Expr
> {
310 let mut array
= None
;
311 let mut filters
= vec
![];
313 for p
in pair
.into_inner() {
315 Rule
::array
=> array
= Some(parse_array(p
)?
),
316 Rule
::filter
=> filters
.push(parse_filter(p
)?
),
317 _
=> unreachable
!("Got {:?}", p
),
321 Ok(Expr { val: array.unwrap(), negated: false, filters }
)
324 fn parse_in_condition_container(pair
: Pair
<Rule
>) -> TeraResult
<Expr
> {
326 for p
in pair
.into_inner() {
328 Rule
::array_filter
=> expr
= Some(parse_array_with_filters(p
)?
),
329 Rule
::dotted_square_bracket_ident
=> {
330 expr
= Some(Expr
::new(ExprVal
::Ident(p
.as_str().to_string())))
332 Rule
::string_expr_filter
=> expr
= Some(parse_string_expr_with_filters(p
)?
),
333 _
=> unreachable
!("Got {:?} in parse_in_condition_container", p
),
339 fn parse_in_condition(pair
: Pair
<Rule
>) -> TeraResult
<Expr
> {
342 let mut negated
= false;
344 for p
in pair
.into_inner() {
347 Rule
::string_expr_filter
=> lhs
= Some(parse_string_expr_with_filters(p
)?
),
348 Rule
::basic_expr_filter
=> lhs
= Some(parse_basic_expr_with_filters(p
)?
),
350 Rule
::in_cond_container
=> rhs
= Some(parse_in_condition_container(p
)?
),
351 Rule
::op_not
=> negated
= true,
352 _
=> unreachable
!("Got {:?} in parse_in_condition", p
),
356 Ok(Expr
::new(ExprVal
::In(In
{
357 lhs
: Box
::new(lhs
.unwrap()),
358 rhs
: Box
::new(rhs
.unwrap()),
363 /// A basic expression with optional filters with prece
364 fn parse_comparison_val(pair
: Pair
<Rule
>) -> TeraResult
<Expr
> {
365 let primary
= |pair
| parse_comparison_val(pair
);
367 let infix
= |lhs
: TeraResult
<Expr
>, op
: Pair
<Rule
>, rhs
: TeraResult
<Expr
>| {
368 Ok(Expr
::new(ExprVal
::Math(MathExpr
{
370 operator
: match op
.as_rule() {
371 Rule
::op_plus
=> MathOperator
::Add
,
372 Rule
::op_minus
=> MathOperator
::Sub
,
373 Rule
::op_times
=> MathOperator
::Mul
,
374 Rule
::op_slash
=> MathOperator
::Div
,
375 Rule
::op_modulo
=> MathOperator
::Modulo
,
382 let expr
= match pair
.as_rule() {
383 Rule
::basic_expr_filter
=> parse_basic_expr_with_filters(pair
)?
,
384 Rule
::comparison_val
=> MATH_CLIMBER
.climb(pair
.into_inner(), primary
, infix
)?
,
385 _
=> unreachable
!("Got {:?} in parse_comparison_val", pair
.as_rule()),
390 fn parse_comparison_expression(pair
: Pair
<Rule
>) -> TeraResult
<Expr
> {
391 let primary
= |pair
| parse_comparison_expression(pair
);
393 let infix
= |lhs
: TeraResult
<Expr
>, op
: Pair
<Rule
>, rhs
: TeraResult
<Expr
>| {
394 Ok(Expr
::new(ExprVal
::Logic(LogicExpr
{
396 operator
: match op
.as_rule() {
397 Rule
::op_lt
=> LogicOperator
::Lt
,
398 Rule
::op_lte
=> LogicOperator
::Lte
,
399 Rule
::op_gt
=> LogicOperator
::Gt
,
400 Rule
::op_gte
=> LogicOperator
::Gte
,
401 Rule
::op_ineq
=> LogicOperator
::NotEq
,
402 Rule
::op_eq
=> LogicOperator
::Eq
,
409 let expr
= match pair
.as_rule() {
410 Rule
::comparison_val
=> parse_comparison_val(pair
)?
,
411 Rule
::string_expr_filter
=> parse_string_expr_with_filters(pair
)?
,
412 Rule
::comparison_expr
=> {
413 COMPARISON_EXPR_CLIMBER
.climb(pair
.into_inner(), primary
, infix
)?
415 _
=> unreachable
!("Got {:?} in parse_comparison_expression", pair
.as_rule()),
420 /// An expression that can be negated
421 fn parse_logic_val(pair
: Pair
<Rule
>) -> TeraResult
<Expr
> {
422 let mut negated
= false;
425 for p
in pair
.into_inner() {
427 Rule
::op_not
=> negated
= true,
428 Rule
::in_cond
=> expr
= Some(parse_in_condition(p
)?
),
429 Rule
::comparison_expr
=> expr
= Some(parse_comparison_expression(p
)?
),
430 Rule
::string_expr_filter
=> expr
= Some(parse_string_expr_with_filters(p
)?
),
435 let mut e
= expr
.unwrap();
440 fn parse_logic_expr(pair
: Pair
<Rule
>) -> TeraResult
<Expr
> {
441 let primary
= |pair
: Pair
<Rule
>| parse_logic_expr(pair
);
443 let infix
= |lhs
: TeraResult
<Expr
>, op
: Pair
<Rule
>, rhs
: TeraResult
<Expr
>| match op
.as_rule() {
444 Rule
::op_or
=> Ok(Expr
::new(ExprVal
::Logic(LogicExpr
{
446 operator
: LogicOperator
::Or
,
449 Rule
::op_and
=> Ok(Expr
::new(ExprVal
::Logic(LogicExpr
{
451 operator
: LogicOperator
::And
,
455 "{:?} not supposed to get there (infix of logic_expression)!",
460 let expr
= match pair
.as_rule() {
461 Rule
::logic_val
=> parse_logic_val(pair
)?
,
462 Rule
::logic_expr
=> LOGIC_EXPR_CLIMBER
.climb(pair
.into_inner(), primary
, infix
)?
,
463 _
=> unreachable
!("Got {:?} in parse_logic_expr", pair
.as_rule()),
468 fn parse_array(pair
: Pair
<Rule
>) -> TeraResult
<ExprVal
> {
469 let mut vals
= vec
![];
471 for p
in pair
.into_inner() {
474 vals
.push(parse_logic_val(p
)?
);
476 _
=> unreachable
!("Got {:?} in parse_array", p
.as_rule()),
480 Ok(ExprVal
::Array(vals
))
483 fn parse_string_array(pair
: Pair
<Rule
>) -> Vec
<String
> {
484 let mut vals
= vec
![];
486 for p
in pair
.into_inner() {
489 vals
.push(replace_string_markers(p
.as_span().as_str()));
491 _
=> unreachable
!("Got {:?} in parse_string_array", p
.as_rule()),
498 fn parse_macro_call(pair
: Pair
<Rule
>) -> TeraResult
<MacroCall
> {
499 let mut namespace
= None
;
501 let mut args
= HashMap
::new();
503 for p
in pair
.into_inner() {
506 // namespace comes first
507 if namespace
.is_none() {
508 namespace
= Some(p
.as_span().as_str().to_string());
510 name
= Some(p
.as_span().as_str().to_string());
514 let (key
, val
) = parse_kwarg(p
)?
;
515 args
.insert(key
, val
);
517 _
=> unreachable
!("Got {:?} in parse_macro_call", p
.as_rule()),
521 Ok(MacroCall { namespace: namespace.unwrap(), name: name.unwrap(), args }
)
524 fn parse_variable_tag(pair
: Pair
<Rule
>) -> TeraResult
<Node
> {
525 let mut ws
= WS
::default();
528 for p
in pair
.into_inner() {
530 Rule
::variable_start
=> {
531 ws
.left
= p
.as_span().as_str() == "{{-";
533 Rule
::variable_end
=> {
534 ws
.right
= p
.as_span().as_str() == "-}}";
536 Rule
::logic_expr
=> expr
= Some(parse_logic_expr(p
)?
),
537 Rule
::array_filter
=> expr
= Some(parse_array_with_filters(p
)?
),
538 _
=> unreachable
!("unexpected {:?} rule in parse_variable_tag", p
.as_rule()),
541 Ok(Node
::VariableBlock(ws
, expr
.unwrap()))
544 fn parse_import_macro(pair
: Pair
<Rule
>) -> Node
{
545 let mut ws
= WS
::default();
547 let mut ident
= None
;
549 for p
in pair
.into_inner() {
552 ws
.left
= p
.as_span().as_str() == "{%-";
554 Rule
::string
=> file
= Some(replace_string_markers(p
.as_span().as_str())),
555 Rule
::ident
=> ident
= Some(p
.as_span().as_str().to_string()),
557 ws
.right
= p
.as_span().as_str() == "-%}";
563 Node
::ImportMacro(ws
, file
.unwrap(), ident
.unwrap())
566 fn parse_extends(pair
: Pair
<Rule
>) -> Node
{
567 let mut ws
= WS
::default();
570 for p
in pair
.into_inner() {
573 ws
.left
= p
.as_span().as_str() == "{%-";
575 Rule
::string
=> file
= Some(replace_string_markers(p
.as_span().as_str())),
577 ws
.right
= p
.as_span().as_str() == "-%}";
583 Node
::Extends(ws
, file
.unwrap())
586 fn parse_include(pair
: Pair
<Rule
>) -> Node
{
587 let mut ws
= WS
::default();
588 let mut files
= vec
![];
589 let mut ignore_missing
= false;
591 for p
in pair
.into_inner() {
594 ws
.left
= p
.as_span().as_str() == "{%-";
597 files
.push(replace_string_markers(p
.as_span().as_str()));
599 Rule
::string_array
=> files
.extend(parse_string_array(p
)),
600 Rule
::ignore_missing
=> ignore_missing
= true,
602 ws
.right
= p
.as_span().as_str() == "-%}";
608 Node
::Include(ws
, files
, ignore_missing
)
611 fn parse_set_tag(pair
: Pair
<Rule
>, global
: bool
) -> TeraResult
<Node
> {
612 let mut ws
= WS
::default();
616 for p
in pair
.into_inner() {
619 ws
.left
= p
.as_span().as_str() == "{%-";
622 ws
.right
= p
.as_span().as_str() == "-%}";
624 Rule
::ident
=> key
= Some(p
.as_str().to_string()),
625 Rule
::logic_expr
=> expr
= Some(parse_logic_expr(p
)?
),
626 Rule
::array_filter
=> expr
= Some(parse_array_with_filters(p
)?
),
627 _
=> unreachable
!("unexpected {:?} rule in parse_set_tag", p
.as_rule()),
631 Ok(Node
::Set(ws
, Set { key: key.unwrap(), value: expr.unwrap(), global }
))
634 fn parse_raw_tag(pair
: Pair
<Rule
>) -> Node
{
635 let mut start_ws
= WS
::default();
636 let mut end_ws
= WS
::default();
639 for p
in pair
.into_inner() {
642 for p2
in p
.into_inner() {
644 Rule
::tag_start
=> start_ws
.left
= p2
.as_span().as_str() == "{%-",
645 Rule
::tag_end
=> start_ws
.right
= p2
.as_span().as_str() == "-%}",
650 Rule
::raw_text
=> text
= Some(p
.as_str().to_string()),
651 Rule
::endraw_tag
=> {
652 for p2
in p
.into_inner() {
654 Rule
::tag_start
=> end_ws
.left
= p2
.as_span().as_str() == "{%-",
655 Rule
::tag_end
=> end_ws
.right
= p2
.as_span().as_str() == "-%}",
660 _
=> unreachable
!("unexpected {:?} rule in parse_raw_tag", p
.as_rule()),
664 Node
::Raw(start_ws
, text
.unwrap(), end_ws
)
667 fn parse_filter_section(pair
: Pair
<Rule
>) -> TeraResult
<Node
> {
668 let mut start_ws
= WS
::default();
669 let mut end_ws
= WS
::default();
670 let mut filter
= None
;
671 let mut body
= vec
![];
673 for p
in pair
.into_inner() {
675 Rule
::filter_tag
=> {
676 for p2
in p
.into_inner() {
678 Rule
::tag_start
=> start_ws
.left
= p2
.as_span().as_str() == "{%-",
679 Rule
::tag_end
=> start_ws
.right
= p2
.as_span().as_str() == "-%}",
680 Rule
::fn_call
=> filter
= Some(parse_fn_call(p2
)?
),
682 filter
= Some(FunctionCall
{
683 name
: p2
.as_str().to_string(),
684 args
: HashMap
::new(),
687 _
=> unreachable
!("Got {:?} while parsing filter_tag", p2
),
692 | Rule
::macro_content
693 | Rule
::block_content
694 | Rule
::filter_section_content
695 | Rule
::for_content
=> {
696 body
.extend(parse_content(p
)?
);
698 Rule
::endfilter_tag
=> {
699 for p2
in p
.into_inner() {
701 Rule
::tag_start
=> end_ws
.left
= p2
.as_span().as_str() == "{%-",
702 Rule
::tag_end
=> end_ws
.right
= p2
.as_span().as_str() == "-%}",
707 _
=> unreachable
!("unexpected {:?} rule in parse_filter_section", p
.as_rule()),
710 Ok(Node
::FilterSection(start_ws
, FilterSection { filter: filter.unwrap(), body }
, end_ws
))
713 fn parse_block(pair
: Pair
<Rule
>) -> TeraResult
<Node
> {
714 let mut start_ws
= WS
::default();
715 let mut end_ws
= WS
::default();
717 let mut body
= vec
![];
719 for p
in pair
.into_inner() {
722 for p2
in p
.into_inner() {
724 Rule
::tag_start
=> start_ws
.left
= p2
.as_span().as_str() == "{%-",
725 Rule
::tag_end
=> start_ws
.right
= p2
.as_span().as_str() == "-%}",
726 Rule
::ident
=> name
= Some(p2
.as_span().as_str().to_string()),
731 Rule
::block_content
=> body
.extend(parse_content(p
)?
),
732 Rule
::endblock_tag
=> {
733 for p2
in p
.into_inner() {
735 Rule
::tag_start
=> end_ws
.left
= p2
.as_span().as_str() == "{%-",
736 Rule
::tag_end
=> end_ws
.right
= p2
.as_span().as_str() == "-%}",
742 _
=> unreachable
!("unexpected {:?} rule in parse_filter_section", p
.as_rule()),
746 Ok(Node
::Block(start_ws
, Block { name: name.unwrap(), body }
, end_ws
))
749 fn parse_macro_arg(p
: Pair
<Rule
>) -> TeraResult
<ExprVal
> {
750 let val
= match p
.as_rule() {
751 Rule
::int
=> Some(ExprVal
::Int(
754 .map_err(|_
| Error
::msg(format
!("Integer out of bounds: `{}`", p
.as_str())))?
,
756 Rule
::float
=> Some(ExprVal
::Float(
759 .map_err(|_
| Error
::msg(format
!("Float out of bounds: `{}`", p
.as_str())))?
,
761 Rule
::boolean
=> match p
.as_str() {
762 "true" => Some(ExprVal
::Bool(true)),
763 "True" => Some(ExprVal
::Bool(true)),
764 "false" => Some(ExprVal
::Bool(false)),
765 "False" => Some(ExprVal
::Bool(false)),
768 Rule
::string
=> Some(ExprVal
::String(replace_string_markers(&p
.as_str()))),
769 _
=> unreachable
!("Got {:?} in parse_macro_arg: {}", p
.as_rule(), p
.as_str()),
775 fn parse_macro_fn(pair
: Pair
<Rule
>) -> TeraResult
<(String
, HashMap
<String
, Option
<Expr
>>)> {
776 let mut name
= String
::new();
777 let mut args
= HashMap
::new();
779 for p2
in pair
.into_inner() {
781 Rule
::ident
=> name
= p2
.as_str().to_string(),
782 Rule
::macro_def_arg
=> {
783 let mut arg_name
= None
;
784 let mut default_val
= None
;
785 for p3
in p2
.into_inner() {
787 Rule
::ident
=> arg_name
= Some(p3
.as_str().to_string()),
788 _
=> default_val
= Some(Expr
::new(parse_macro_arg(p3
)?
)),
791 args
.insert(arg_name
.unwrap(), default_val
);
800 fn parse_macro_definition(pair
: Pair
<Rule
>) -> TeraResult
<Node
> {
801 let mut start_ws
= WS
::default();
802 let mut end_ws
= WS
::default();
803 let mut name
= String
::new();
804 let mut args
= HashMap
::new();
805 let mut body
= vec
![];
807 for p
in pair
.into_inner() {
810 for p2
in p
.into_inner() {
812 Rule
::tag_start
=> start_ws
.left
= p2
.as_span().as_str() == "{%-",
813 Rule
::tag_end
=> start_ws
.right
= p2
.as_span().as_str() == "-%}",
814 Rule
::macro_fn_wrapper
=> {
815 let macro_fn
= parse_macro_fn(p2
)?
;
823 Rule
::macro_content
=> body
.extend(parse_content(p
)?
),
824 Rule
::endmacro_tag
=> {
825 for p2
in p
.into_inner() {
827 Rule
::tag_start
=> end_ws
.left
= p2
.as_span().as_str() == "{%-",
828 Rule
::tag_end
=> end_ws
.right
= p2
.as_span().as_str() == "-%}",
834 _
=> unreachable
!("unexpected {:?} rule in parse_macro_definition", p
.as_rule()),
838 Ok(Node
::MacroDefinition(start_ws
, MacroDefinition { name, args, body }
, end_ws
))
841 fn parse_forloop(pair
: Pair
<Rule
>) -> TeraResult
<Node
> {
842 let mut start_ws
= WS
::default();
843 let mut end_ws
= WS
::default();
846 let mut value
= None
;
847 let mut container
= None
;
848 let mut body
= vec
![];
849 let mut empty_body
: Option
<Vec
<Node
>> = None
;
851 for p
in pair
.into_inner() {
854 let mut idents
= vec
![];
855 for p2
in p
.into_inner() {
857 Rule
::tag_start
=> start_ws
.left
= p2
.as_span().as_str() == "{%-",
858 Rule
::tag_end
=> start_ws
.right
= p2
.as_span().as_str() == "-%}",
859 Rule
::ident
=> idents
.push(p2
.as_str().to_string()),
860 Rule
::basic_expr_filter
=> {
861 container
= Some(parse_basic_expr_with_filters(p2
)?
);
863 Rule
::array_filter
=> container
= Some(parse_array_with_filters(p2
)?
),
868 if idents
.len() == 1 {
869 value
= Some(idents
[0].clone());
871 key
= Some(idents
[0].clone());
872 value
= Some(idents
[1].clone());
876 | Rule
::macro_content
877 | Rule
::block_content
878 | Rule
::filter_section_content
879 | Rule
::for_content
=> {
881 Some(ref mut empty_body
) => empty_body
.extend(parse_content(p
)?
),
882 None
=> body
.extend(parse_content(p
)?
),
886 empty_body
= Some(vec
![]);
888 Rule
::endfor_tag
=> {
889 for p2
in p
.into_inner() {
891 Rule
::tag_start
=> end_ws
.left
= p2
.as_span().as_str() == "{%-",
892 Rule
::tag_end
=> end_ws
.right
= p2
.as_span().as_str() == "-%}",
898 _
=> unreachable
!("unexpected {:?} rule in parse_forloop", p
.as_rule()),
904 Forloop { key, value: value.unwrap(), container: container.unwrap(), body, empty_body }
,
909 fn parse_break_tag(pair
: Pair
<Rule
>) -> Node
{
910 let mut ws
= WS
::default();
912 for p
in pair
.into_inner() {
915 ws
.left
= p
.as_span().as_str() == "{%-";
918 ws
.right
= p
.as_span().as_str() == "-%}";
927 fn parse_continue_tag(pair
: Pair
<Rule
>) -> Node
{
928 let mut ws
= WS
::default();
930 for p
in pair
.into_inner() {
933 ws
.left
= p
.as_span().as_str() == "{%-";
936 ws
.right
= p
.as_span().as_str() == "-%}";
945 fn parse_comment_tag(pair
: Pair
<Rule
>) -> Node
{
946 let mut ws
= WS
::default();
947 let mut content
= String
::new();
949 for p
in pair
.into_inner() {
951 Rule
::comment_start
=> {
952 ws
.left
= p
.as_span().as_str() == "{#-";
954 Rule
::comment_end
=> {
955 ws
.right
= p
.as_span().as_str() == "-#}";
957 Rule
::comment_text
=> {
958 content
= p
.as_str().to_owned();
964 Node
::Comment(ws
, content
)
967 fn parse_if(pair
: Pair
<Rule
>) -> TeraResult
<Node
> {
968 // the `endif` tag ws handling
969 let mut end_ws
= WS
::default();
970 let mut conditions
= vec
![];
971 let mut otherwise
= None
;
973 // the current node we're exploring
974 let mut current_ws
= WS
::default();
976 let mut current_body
= vec
![];
977 let mut in_else
= false;
979 for p
in pair
.into_inner() {
981 Rule
::if_tag
| Rule
::elif_tag
=> {
982 // Reset everything for elifs
983 if p
.as_rule() == Rule
::elif_tag
{
984 conditions
.push((current_ws
, expr
.unwrap(), current_body
));
986 current_ws
= WS
::default();
987 current_body
= vec
![];
990 for p2
in p
.into_inner() {
992 Rule
::tag_start
=> current_ws
.left
= p2
.as_span().as_str() == "{%-",
993 Rule
::tag_end
=> current_ws
.right
= p2
.as_span().as_str() == "-%}",
994 Rule
::logic_expr
=> expr
= Some(parse_logic_expr(p2
)?
),
1000 | Rule
::macro_content
1001 | Rule
::block_content
1003 | Rule
::filter_section_content
=> current_body
.extend(parse_content(p
)?
),
1005 // had an elif before the else
1007 conditions
.push((current_ws
, expr
.unwrap(), current_body
));
1009 current_ws
= WS
::default();
1010 current_body
= vec
![];
1013 for p2
in p
.into_inner() {
1014 match p2
.as_rule() {
1015 Rule
::tag_start
=> current_ws
.left
= p2
.as_span().as_str() == "{%-",
1016 Rule
::tag_end
=> current_ws
.right
= p2
.as_span().as_str() == "-%}",
1017 _
=> unreachable
!(),
1021 Rule
::endif_tag
=> {
1023 otherwise
= Some((current_ws
, current_body
));
1026 conditions
.push((current_ws
, expr
.unwrap(), current_body
));
1029 for p2
in p
.into_inner() {
1030 match p2
.as_rule() {
1031 Rule
::tag_start
=> end_ws
.left
= p2
.as_span().as_str() == "{%-",
1032 Rule
::tag_end
=> end_ws
.right
= p2
.as_span().as_str() == "-%}",
1033 _
=> unreachable
!(),
1038 _
=> unreachable
!("unreachable rule in parse_if: {:?}", p
.as_rule()),
1042 Ok(Node
::If(If { conditions, otherwise }
, end_ws
))
1045 fn parse_content(pair
: Pair
<Rule
>) -> TeraResult
<Vec
<Node
>> {
1046 let mut nodes
= vec
![];
1048 for p
in pair
.into_inner() {
1050 Rule
::include_tag
=> nodes
.push(parse_include(p
)),
1051 Rule
::comment_tag
=> nodes
.push(parse_comment_tag(p
)),
1052 Rule
::super_tag
=> nodes
.push(Node
::Super
),
1053 Rule
::set_tag
=> nodes
.push(parse_set_tag(p
, false)?
),
1054 Rule
::set_global_tag
=> nodes
.push(parse_set_tag(p
, true)?
),
1055 Rule
::raw
=> nodes
.push(parse_raw_tag(p
)),
1056 Rule
::variable_tag
=> nodes
.push(parse_variable_tag(p
)?
),
1057 Rule
::macro_definition
=> nodes
.push(parse_macro_definition(p
)?
),
1058 Rule
::forloop
=> nodes
.push(parse_forloop(p
)?
),
1059 Rule
::break_tag
=> nodes
.push(parse_break_tag(p
)),
1060 Rule
::continue_tag
=> nodes
.push(parse_continue_tag(p
)),
1065 | Rule
::filter_section_if
=> nodes
.push(parse_if(p
)?
),
1066 Rule
::filter_section
=> nodes
.push(parse_filter_section(p
)?
),
1067 Rule
::text
=> nodes
.push(Node
::Text(p
.as_span().as_str().to_string())),
1068 Rule
::block
=> nodes
.push(parse_block(p
)?
),
1069 _
=> unreachable
!("unreachable content rule: {:?}", p
.as_rule()),
1076 pub fn parse(input
: &str) -> TeraResult
<Vec
<Node
>> {
1077 let mut pairs
= match TeraParser
::parse(Rule
::template
, input
) {
1080 let fancy_e
= e
.renamed_rules(|rule
| {
1082 Rule
::EOI
=> "end of input".to_string(),
1083 Rule
::int
=> "an integer".to_string(),
1084 Rule
::float
=> "a float".to_string(),
1086 | Rule
::double_quoted_string
1087 | Rule
::single_quoted_string
1088 | Rule
::backquoted_quoted_string
=> {
1089 "a string".to_string()
1091 Rule
::string_concat
=> "a concatenation of strings".to_string(),
1092 Rule
::string_expr_filter
=> "a string or a concatenation of strings".to_string(),
1093 Rule
::all_chars
=> "a character".to_string(),
1094 Rule
::array
=> "an array of values".to_string(),
1095 Rule
::array_filter
=> "an array of values with an optional filter".to_string(),
1096 Rule
::string_array
=> "an array of strings".to_string(),
1097 Rule
::basic_val
=> "a value".to_string(),
1098 Rule
::basic_op
=> "a mathematical operator".to_string(),
1099 Rule
::comparison_op
=> "a comparison operator".to_string(),
1100 Rule
::boolean
=> "`true` or `false`".to_string(),
1101 Rule
::ident
=> "an identifier (must start with a-z)".to_string(),
1102 Rule
::dotted_ident
=> "a dotted identifier (identifiers separated by `.`)".to_string(),
1103 Rule
::dotted_square_bracket_ident
=> "a square bracketed identifier (identifiers separated by `.` or `[]`s)".to_string(),
1104 Rule
::square_brackets
=> "an identifier, string or integer inside `[]`s".to_string(),
1105 Rule
::basic_expr_filter
=> "an expression with an optional filter".to_string(),
1106 Rule
::comparison_val
=> "a comparison value".to_string(),
1107 Rule
::basic_expr
| Rule
::comparison_expr
=> "an expression".to_string(),
1108 Rule
::logic_val
=> "a value that can be negated".to_string(),
1109 Rule
::logic_expr
=> "any expressions".to_string(),
1110 Rule
::fn_call
=> "a function call".to_string(),
1111 Rule
::kwarg
=> "a keyword argument: `key=value` where `value` can be any expressions".to_string(),
1112 Rule
::kwargs
=> "a list of keyword arguments: `key=value` where `value` can be any expressions and separated by `,`".to_string(),
1113 Rule
::op_or
=> "`or`".to_string(),
1114 Rule
::op_and
=> "`and`".to_string(),
1115 Rule
::op_not
=> "`not`".to_string(),
1116 Rule
::op_lte
=> "`<=`".to_string(),
1117 Rule
::op_gte
=> "`>=`".to_string(),
1118 Rule
::op_lt
=> "`<`".to_string(),
1119 Rule
::op_gt
=> "`>`".to_string(),
1120 Rule
::op_ineq
=> "`!=`".to_string(),
1121 Rule
::op_eq
=> "`==`".to_string(),
1122 Rule
::op_plus
=> "`+`".to_string(),
1123 Rule
::op_minus
=> "`-`".to_string(),
1124 Rule
::op_times
=> "`*`".to_string(),
1125 Rule
::op_slash
=> "`/`".to_string(),
1126 Rule
::op_modulo
=> "`%`".to_string(),
1127 Rule
::filter
=> "a filter".to_string(),
1128 Rule
::test
=> "a test".to_string(),
1129 Rule
::test_not
=> "a negated test".to_string(),
1130 Rule
::test_call
=> "a test call".to_string(),
1131 Rule
::test_arg
=> "a test argument (any expressions including arrays)".to_string(),
1132 Rule
::test_args
=> "a list of test arguments (any expression including arrayss)".to_string(),
1133 Rule
::macro_fn
| Rule
::macro_fn_wrapper
=> "a macro function".to_string(),
1134 Rule
::macro_call
=> "a macro function call".to_string(),
1135 Rule
::macro_def_arg
=> {
1136 "an argument name with an optional default literal value: `id`, `key=1`".to_string()
1138 Rule
::macro_def_args
=> {
1139 "a list of argument names with an optional default literal value: `id`, `key=1`".to_string()
1141 Rule
::endmacro_tag
=> "`{% endmacro %}`".to_string(),
1142 Rule
::macro_content
=> "the macro content".to_string(),
1143 Rule
::filter_section_content
=> "the filter section content".to_string(),
1144 Rule
::set_tag
=> "a `set` tag`".to_string(),
1145 Rule
::set_global_tag
=> "a `set_global` tag`".to_string(),
1146 Rule
::block_content
| Rule
::content
| Rule
::for_content
=> {
1147 "some content".to_string()
1149 Rule
::text
=> "some text".to_string(),
1150 // Pest will error an unexpected tag as Rule::tag_start
1151 // and just showing `{%` is not clear as some other valid
1152 // tags will also start with `{%`
1153 Rule
::tag_start
=> "tag".to_string(),
1154 Rule
::tag_end
=> "`%}` or `-%}`".to_string(),
1155 Rule
::super_tag
=> "`{{ super() }}`".to_string(),
1156 Rule
::raw_tag
=> "`{% raw %}`".to_string(),
1157 Rule
::raw_text
=> "some raw text".to_string(),
1158 Rule
::raw
=> "a raw block (`{% raw %}...{% endraw %}`".to_string(),
1159 Rule
::endraw_tag
=> "`{% endraw %}`".to_string(),
1160 Rule
::ignore_missing
=> "ignore missing mark for include tag".to_string(),
1161 Rule
::include_tag
=> r
#"an include tag (`{% include "..." %}`)"#.to_string(),
1162 Rule
::comment_tag
=> "a comment tag (`{#...#}`)".to_string(),
1163 Rule
::comment_text
=> "the context of a comment (`{# ... #}`)".to_string(),
1164 Rule
::variable_tag
=> "a variable tag (`{{ ... }}`)".to_string(),
1165 Rule
::filter_tag
| Rule
::filter_section
=> {
1166 "a filter section (`{% filter something %}...{% endfilter %}`)".to_string()
1168 Rule
::for_tag
| Rule
::forloop
=> {
1169 "a forloop (`{% for i in something %}...{% endfor %}".to_string()
1171 Rule
::endfilter_tag
=> "an endfilter tag (`{% endfilter %}`)".to_string(),
1172 Rule
::endfor_tag
=> "an endfor tag (`{% endfor %}`)".to_string(),
1178 | Rule
::filter_section_if
=> {
1179 "a `if` tag".to_string()
1181 Rule
::elif_tag
=> "an `elif` tag".to_string(),
1182 Rule
::else_tag
=> "an `else` tag".to_string(),
1183 Rule
::endif_tag
=> "an endif tag (`{% endif %}`)".to_string(),
1184 Rule
::WHITESPACE
=> "whitespace".to_string(),
1185 Rule
::variable_start
=> "a variable start (`{{`)".to_string(),
1186 Rule
::variable_end
=> "a variable end (`}}`)".to_string(),
1187 Rule
::comment_start
=> "a comment start (`{#`)".to_string(),
1188 Rule
::comment_end
=> "a comment end (`#}`)".to_string(),
1189 Rule
::block_start
=> "`{{`, `{%` or `{#`".to_string(),
1190 Rule
::import_macro_tag
=> r
#"an import macro tag (`{% import "filename" as namespace %}`"#.to_string(),
1191 Rule
::block
| Rule
::block_tag
=> r
#"a block tag (`{% block block_name %}`"#.to_string(),
1192 Rule
::endblock_tag
=> r
#"an endblock tag (`{% endblock block_name %}`"#.to_string(),
1193 Rule
::macro_definition
1194 | Rule
::macro_tag
=> r
#"a macro definition tag (`{% macro my_macro() %}`"#.to_string(),
1195 Rule
::extends_tag
=> r
#"an extends tag (`{% extends "myfile" %}`"#.to_string(),
1196 Rule
::template
=> "a template".to_string(),
1197 Rule
::break_tag
=> "a break tag".to_string(),
1198 Rule
::continue_tag
=> "a continue tag".to_string(),
1199 Rule
::top_imports
=> "top imports".to_string(),
1200 Rule
::in_cond
=> "a `in` condition".to_string(),
1201 Rule
::in_cond_container
=> "a `in` condition container: a string, an array or an ident".to_string(),
1204 return Err(Error
::msg(fancy_e
));
1208 let mut nodes
= vec
![];
1210 // We must have at least a `template` pair if we got there
1211 for p
in pairs
.next().unwrap().into_inner() {
1213 Rule
::extends_tag
=> nodes
.push(parse_extends(p
)),
1214 Rule
::import_macro_tag
=> nodes
.push(parse_import_macro(p
)),
1215 Rule
::content
=> nodes
.extend(parse_content(p
)?
),
1216 Rule
::comment_tag
=> (),
1218 _
=> unreachable
!("unknown tpl rule: {:?}", p
.as_rule()),