1use std::collections::HashMap;
2
3use lazy_static::lazy_static;
4use pest::iterators::Pair;
5use crate::errors::{Error, Result as TeraResult};
7use pest::prec_climber::{Assoc, Operator, PrecClimber};
8use pest::Parser;
9use pest_derive::Parser;
10
11const _GRAMMAR: &str = include_str!("tera.pest");
14
15#[derive(Parser)]
16#[grammar = "parser/tera.pest"]
17pub struct TeraParser;
18
19pub mod ast;
21mod whitespace;
22
23#[cfg(test)]
24mod tests;
25
26use self::ast::*;
27pub use self::whitespace::remove_whitespace;
28
29lazy_static! {
30 static ref MATH_CLIMBER: PrecClimber<Rule> = PrecClimber::new(vec![
31 Operator::new(Rule::op_plus, Assoc::Left) | Operator::new(Rule::op_minus, Assoc::Left),
33 Operator::new(Rule::op_times, Assoc::Left) |
35 Operator::new(Rule::op_slash, Assoc::Left) |
36 Operator::new(Rule::op_modulo, Assoc::Left),
37 ]);
38 static ref COMPARISON_EXPR_CLIMBER: PrecClimber<Rule> = PrecClimber::new(vec![
39 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),
43 ]);
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),
47 ]);
48}
49
50fn 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"),
58 }
59}
60
61fn parse_kwarg(pair: Pair<Rule>) -> TeraResult<(String, Expr)> {
62 let mut name = None;
63 let mut val = None;
64
65 for p in pair.into_inner() {
66 match p.as_rule() {
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()),
71 };
72 }
73
74 Ok((name.unwrap(), val.unwrap()))
75}
76
77fn parse_fn_call(pair: Pair<Rule>) -> TeraResult<FunctionCall> {
78 let mut name = None;
79 let mut args = HashMap::new();
80
81 for p in pair.into_inner() {
82 match p.as_rule() {
83 Rule::ident => name = Some(p.as_span().as_str().to_string()),
84 Rule::kwarg => {
85 let (name, val) = parse_kwarg(p)?;
86 args.insert(name, val);
87 }
88 _ => unreachable!(
89 "{:?} not supposed to get there (parse_fn_call)!",
90 p.as_rule()
91 ),
92 };
93 }
94
95 Ok(FunctionCall {
96 name: name.unwrap(),
97 args,
98 })
99}
100
101fn parse_filter(pair: Pair<Rule>) -> TeraResult<FunctionCall> {
102 let mut name = None;
103 let mut args = HashMap::new();
104 for p in pair.into_inner() {
105 match p.as_rule() {
106 Rule::ident => name = Some(p.as_span().as_str().to_string()),
107 Rule::kwarg => {
108 let (name, val) = parse_kwarg(p)?;
109 args.insert(name, val);
110 }
111 Rule::fn_call => {
112 return parse_fn_call(p);
113 }
114 _ => unreachable!(
115 "{:?} not supposed to get there (parse_filter)!",
116 p.as_rule()
117 ),
118 };
119 }
120
121 Ok(FunctionCall {
122 name: name.unwrap(),
123 args,
124 })
125}
126
127fn parse_test_call(pair: Pair<Rule>) -> TeraResult<(String, Vec<Expr>)> {
128 let mut name = None;
129 let mut args = vec![];
130
131 for p in pair.into_inner() {
132 match p.as_rule() {
133 Rule::ident => name = Some(p.as_span().as_str().to_string()),
134 Rule::test_arg =>
135 {
137 for p2 in p.into_inner() {
138 match p2.as_rule() {
139 Rule::logic_expr => {
140 args.push(parse_logic_expr(p2)?);
141 }
142 Rule::array => {
143 args.push(Expr::new(parse_array(p2)?));
144 }
145 _ => unreachable!("Invalid arg type for test {:?}", p2.as_rule()),
146 }
147 }
148 }
149 _ => unreachable!(
150 "{:?} not supposed to get there (parse_test_call)!",
151 p.as_rule()
152 ),
153 };
154 }
155
156 Ok((name.unwrap(), args))
157}
158
159fn parse_test(pair: Pair<Rule>) -> TeraResult<Test> {
160 let mut ident = None;
161 let mut name = None;
162 let mut args = vec![];
163
164 for p in pair.into_inner() {
165 match p.as_rule() {
166 Rule::dotted_ident => ident = Some(p.as_str().to_string()),
167 Rule::test_call => {
168 let (_name, _args) = parse_test_call(p)?;
169 name = Some(_name);
170 args = _args;
171 }
172 _ => unreachable!("{:?} not supposed to get there (parse_ident)!", p.as_rule()),
173 };
174 }
175
176 Ok(Test {
177 ident: ident.unwrap(),
178 negated: false,
179 name: name.unwrap(),
180 args,
181 })
182}
183
184fn parse_string_concat(pair: Pair<Rule>) -> TeraResult<ExprVal> {
185 let mut values = vec![];
186 let mut current_str = String::new();
187
188 for p in pair.into_inner() {
190 match p.as_rule() {
191 Rule::string => {
192 current_str.push_str(&replace_string_markers(p.as_str()));
193 }
194 Rule::int => {
195 if !current_str.is_empty() {
196 values.push(ExprVal::String(current_str));
197 current_str = String::new();
198 }
199 values.push(ExprVal::Int(p.as_str().parse().map_err(|_| {
200 Error::msg(format!("Integer out of bounds: `{}`", p.as_str()))
201 })?));
202 }
203 Rule::float => {
204 if !current_str.is_empty() {
205 values.push(ExprVal::String(current_str));
206 current_str = String::new();
207 }
208 values.push(ExprVal::Float(p.as_str().parse().map_err(|_| {
209 Error::msg(format!("Float out of bounds: `{}`", p.as_str()))
210 })?));
211 }
212 Rule::dotted_square_bracket_ident => {
213 if !current_str.is_empty() {
214 values.push(ExprVal::String(current_str));
215 current_str = String::new();
216 }
217 values.push(ExprVal::Ident(p.as_str().to_string()))
218 }
219 Rule::fn_call => {
220 if !current_str.is_empty() {
221 values.push(ExprVal::String(current_str));
222 current_str = String::new();
223 }
224 values.push(ExprVal::FunctionCall(parse_fn_call(p)?))
225 }
226 _ => unreachable!("Got {:?} in parse_string_concat", p),
227 };
228 }
229
230 if values.is_empty() {
231 return Ok(ExprVal::String(current_str));
233 }
234
235 if !current_str.is_empty() {
236 values.push(ExprVal::String(current_str));
237 }
238
239 Ok(ExprVal::StringConcat(StringConcat { values }))
240}
241
242fn parse_basic_expression(pair: Pair<Rule>) -> TeraResult<ExprVal> {
243 let primary = parse_basic_expression;
244
245 let infix = |lhs: TeraResult<ExprVal>, op: Pair<Rule>, rhs: TeraResult<ExprVal>| {
246 Ok(ExprVal::Math(MathExpr {
247 lhs: Box::new(Expr::new(lhs?)),
248 operator: match op.as_rule() {
249 Rule::op_plus => MathOperator::Add,
250 Rule::op_minus => MathOperator::Sub,
251 Rule::op_times => MathOperator::Mul,
252 Rule::op_slash => MathOperator::Div,
253 Rule::op_modulo => MathOperator::Modulo,
254 _ => unreachable!(),
255 },
256 rhs: Box::new(Expr::new(rhs?)),
257 }))
258 };
259
260 let expr = match pair.as_rule() {
261 Rule::int => ExprVal::Int(
262 pair.as_str()
263 .parse()
264 .map_err(|_| Error::msg(format!("Integer out of bounds: `{}`", pair.as_str())))?,
265 ),
266 Rule::float => ExprVal::Float(
267 pair.as_str()
268 .parse()
269 .map_err(|_| Error::msg(format!("Float out of bounds: `{}`", pair.as_str())))?,
270 ),
271 Rule::boolean => match pair.as_str() {
272 "true" => ExprVal::Bool(true),
273 "True" => ExprVal::Bool(true),
274 "false" => ExprVal::Bool(false),
275 "False" => ExprVal::Bool(false),
276 _ => unreachable!(),
277 },
278 Rule::test => ExprVal::Test(parse_test(pair)?),
279 Rule::test_not => {
280 let mut test = parse_test(pair)?;
281 test.negated = true;
282 ExprVal::Test(test)
283 }
284 Rule::fn_call => ExprVal::FunctionCall(parse_fn_call(pair)?),
285 Rule::macro_call => ExprVal::MacroCall(parse_macro_call(pair)?),
286 Rule::dotted_square_bracket_ident => ExprVal::Ident(pair.as_str().to_string()),
287 Rule::basic_expr => MATH_CLIMBER.climb(pair.into_inner(), primary, infix)?,
288 _ => unreachable!(
289 "Got {:?} in parse_basic_expression: {}",
290 pair.as_rule(),
291 pair.as_str()
292 ),
293 };
294 Ok(expr)
295}
296
297fn parse_basic_expr_with_filters(pair: Pair<Rule>) -> TeraResult<Expr> {
299 let mut expr_val = None;
300 let mut filters = vec![];
301
302 for p in pair.into_inner() {
303 match p.as_rule() {
304 Rule::basic_expr => expr_val = Some(parse_basic_expression(p)?),
305 Rule::filter => filters.push(parse_filter(p)?),
306 _ => unreachable!("Got {:?}", p),
307 };
308 }
309
310 Ok(Expr {
311 val: expr_val.unwrap(),
312 negated: false,
313 filters,
314 })
315}
316
317fn parse_string_expr_with_filters(pair: Pair<Rule>) -> TeraResult<Expr> {
319 let mut expr_val = None;
320 let mut filters = vec![];
321
322 for p in pair.into_inner() {
323 match p.as_rule() {
324 Rule::string => expr_val = Some(ExprVal::String(replace_string_markers(p.as_str()))),
325 Rule::string_concat => expr_val = Some(parse_string_concat(p)?),
326 Rule::filter => filters.push(parse_filter(p)?),
327 _ => unreachable!("Got {:?}", p),
328 };
329 }
330
331 Ok(Expr {
332 val: expr_val.unwrap(),
333 negated: false,
334 filters,
335 })
336}
337
338fn parse_array_with_filters(pair: Pair<Rule>) -> TeraResult<Expr> {
340 let mut array = None;
341 let mut filters = vec![];
342
343 for p in pair.into_inner() {
344 match p.as_rule() {
345 Rule::array => array = Some(parse_array(p)?),
346 Rule::filter => filters.push(parse_filter(p)?),
347 _ => unreachable!("Got {:?}", p),
348 };
349 }
350
351 Ok(Expr {
352 val: array.unwrap(),
353 negated: false,
354 filters,
355 })
356}
357
358fn parse_in_condition_container(pair: Pair<Rule>) -> TeraResult<Expr> {
359 let mut expr = None;
360 for p in pair.into_inner() {
361 match p.as_rule() {
362 Rule::array_filter => expr = Some(parse_array_with_filters(p)?),
363 Rule::dotted_square_bracket_ident => {
364 expr = Some(Expr::new(ExprVal::Ident(p.as_str().to_string())))
365 }
366 Rule::string_expr_filter => expr = Some(parse_string_expr_with_filters(p)?),
367 _ => unreachable!("Got {:?} in parse_in_condition_container", p),
368 };
369 }
370 Ok(expr.unwrap())
371}
372
373fn parse_in_condition(pair: Pair<Rule>) -> TeraResult<Expr> {
374 let mut lhs = None;
375 let mut rhs = None;
376 let mut negated = false;
377
378 for p in pair.into_inner() {
379 match p.as_rule() {
380 Rule::string_expr_filter => lhs = Some(parse_string_expr_with_filters(p)?),
382 Rule::basic_expr_filter => lhs = Some(parse_basic_expr_with_filters(p)?),
383 Rule::in_cond_container => rhs = Some(parse_in_condition_container(p)?),
385 Rule::op_not => negated = true,
386 _ => unreachable!("Got {:?} in parse_in_condition", p),
387 };
388 }
389
390 Ok(Expr::new(ExprVal::In(In {
391 lhs: Box::new(lhs.unwrap()),
392 rhs: Box::new(rhs.unwrap()),
393 negated,
394 })))
395}
396
397fn parse_comparison_val(pair: Pair<Rule>) -> TeraResult<Expr> {
399 let primary = parse_comparison_val;
400
401 let infix = |lhs: TeraResult<Expr>, op: Pair<Rule>, rhs: TeraResult<Expr>| {
402 Ok(Expr::new(ExprVal::Math(MathExpr {
403 lhs: Box::new(lhs?),
404 operator: match op.as_rule() {
405 Rule::op_plus => MathOperator::Add,
406 Rule::op_minus => MathOperator::Sub,
407 Rule::op_times => MathOperator::Mul,
408 Rule::op_slash => MathOperator::Div,
409 Rule::op_modulo => MathOperator::Modulo,
410 _ => unreachable!(),
411 },
412 rhs: Box::new(rhs?),
413 })))
414 };
415
416 let expr = match pair.as_rule() {
417 Rule::basic_expr_filter => parse_basic_expr_with_filters(pair)?,
418 Rule::comparison_val => MATH_CLIMBER.climb(pair.into_inner(), primary, infix)?,
419 _ => unreachable!("Got {:?} in parse_comparison_val", pair.as_rule()),
420 };
421 Ok(expr)
422}
423
424fn parse_comparison_expression(pair: Pair<Rule>) -> TeraResult<Expr> {
425 let primary = parse_comparison_expression;
426
427 let infix = |lhs: TeraResult<Expr>, op: Pair<Rule>, rhs: TeraResult<Expr>| {
428 Ok(Expr::new(ExprVal::Logic(LogicExpr {
429 lhs: Box::new(lhs?),
430 operator: match op.as_rule() {
431 Rule::op_lt => LogicOperator::Lt,
432 Rule::op_lte => LogicOperator::Lte,
433 Rule::op_gt => LogicOperator::Gt,
434 Rule::op_gte => LogicOperator::Gte,
435 Rule::op_ineq => LogicOperator::NotEq,
436 Rule::op_eq => LogicOperator::Eq,
437 _ => unreachable!(),
438 },
439 rhs: Box::new(rhs?),
440 })))
441 };
442
443 let expr = match pair.as_rule() {
444 Rule::comparison_val => parse_comparison_val(pair)?,
445 Rule::string_expr_filter => parse_string_expr_with_filters(pair)?,
446 Rule::comparison_expr => {
447 COMPARISON_EXPR_CLIMBER.climb(pair.into_inner(), primary, infix)?
448 }
449 _ => unreachable!("Got {:?} in parse_comparison_expression", pair.as_rule()),
450 };
451 Ok(expr)
452}
453
454fn parse_logic_val(pair: Pair<Rule>) -> TeraResult<Expr> {
456 let mut negated = false;
457 let mut expr = None;
458
459 for p in pair.into_inner() {
460 match p.as_rule() {
461 Rule::op_not => negated = true,
462 Rule::in_cond => expr = Some(parse_in_condition(p)?),
463 Rule::comparison_expr => expr = Some(parse_comparison_expression(p)?),
464 Rule::string_expr_filter => expr = Some(parse_string_expr_with_filters(p)?),
465 _ => unreachable!(),
466 };
467 }
468
469 let mut e = expr.unwrap();
470 e.negated = negated;
471 Ok(e)
472}
473
474fn parse_logic_expr(pair: Pair<Rule>) -> TeraResult<Expr> {
475 let primary = parse_logic_expr;
476
477 let infix = |lhs: TeraResult<Expr>, op: Pair<Rule>, rhs: TeraResult<Expr>| match op.as_rule() {
478 Rule::op_or => Ok(Expr::new(ExprVal::Logic(LogicExpr {
479 lhs: Box::new(lhs?),
480 operator: LogicOperator::Or,
481 rhs: Box::new(rhs?),
482 }))),
483 Rule::op_and => Ok(Expr::new(ExprVal::Logic(LogicExpr {
484 lhs: Box::new(lhs?),
485 operator: LogicOperator::And,
486 rhs: Box::new(rhs?),
487 }))),
488 _ => unreachable!(
489 "{:?} not supposed to get there (infix of logic_expression)!",
490 op.as_rule()
491 ),
492 };
493
494 let expr = match pair.as_rule() {
495 Rule::logic_val => parse_logic_val(pair)?,
496 Rule::logic_expr => LOGIC_EXPR_CLIMBER.climb(pair.into_inner(), primary, infix)?,
497 _ => unreachable!("Got {:?} in parse_logic_expr", pair.as_rule()),
498 };
499 Ok(expr)
500}
501
502fn parse_array(pair: Pair<Rule>) -> TeraResult<ExprVal> {
503 let mut vals = vec![];
504
505 for p in pair.into_inner() {
506 match p.as_rule() {
507 Rule::logic_val => {
508 vals.push(parse_logic_val(p)?);
509 }
510 _ => unreachable!("Got {:?} in parse_array", p.as_rule()),
511 }
512 }
513
514 Ok(ExprVal::Array(vals))
515}
516
517fn parse_string_array(pair: Pair<Rule>) -> Vec<String> {
518 let mut vals = vec![];
519
520 for p in pair.into_inner() {
521 match p.as_rule() {
522 Rule::string => {
523 vals.push(replace_string_markers(p.as_span().as_str()));
524 }
525 _ => unreachable!("Got {:?} in parse_string_array", p.as_rule()),
526 }
527 }
528
529 vals
530}
531
532fn parse_macro_call(pair: Pair<Rule>) -> TeraResult<MacroCall> {
533 let mut namespace = None;
534 let mut name = None;
535 let mut args = HashMap::new();
536
537 for p in pair.into_inner() {
538 match p.as_rule() {
539 Rule::ident => {
540 if namespace.is_none() {
542 namespace = Some(p.as_span().as_str().to_string());
543 } else {
544 name = Some(p.as_span().as_str().to_string());
545 }
546 }
547 Rule::kwarg => {
548 let (key, val) = parse_kwarg(p)?;
549 args.insert(key, val);
550 }
551 _ => unreachable!("Got {:?} in parse_macro_call", p.as_rule()),
552 }
553 }
554
555 Ok(MacroCall {
556 namespace: namespace.unwrap(),
557 name: name.unwrap(),
558 args,
559 })
560}
561
562fn parse_variable_tag(pair: Pair<Rule>) -> TeraResult<Node> {
563 let mut ws = WS::default();
564 let mut expr = None;
565
566 for p in pair.into_inner() {
567 match p.as_rule() {
568 Rule::variable_start => {
569 ws.left = p.as_span().as_str() == "{{-";
570 }
571 Rule::variable_end => {
572 ws.right = p.as_span().as_str() == "-}}";
573 }
574 Rule::logic_expr => expr = Some(parse_logic_expr(p)?),
575 Rule::array_filter => expr = Some(parse_array_with_filters(p)?),
576 _ => unreachable!("unexpected {:?} rule in parse_variable_tag", p.as_rule()),
577 }
578 }
579 Ok(Node::VariableBlock(ws, expr.unwrap()))
580}
581
582fn parse_import_macro(pair: Pair<Rule>) -> Node {
583 let mut ws = WS::default();
584 let mut file = None;
585 let mut ident = None;
586
587 for p in pair.into_inner() {
588 match p.as_rule() {
589 Rule::tag_start => {
590 ws.left = p.as_span().as_str() == "{%-";
591 }
592 Rule::string => file = Some(replace_string_markers(p.as_span().as_str())),
593 Rule::ident => ident = Some(p.as_span().as_str().to_string()),
594 Rule::tag_end => {
595 ws.right = p.as_span().as_str() == "-%}";
596 }
597 _ => unreachable!(),
598 };
599 }
600
601 Node::ImportMacro(ws, file.unwrap(), ident.unwrap())
602}
603
604fn parse_extends(pair: Pair<Rule>) -> Node {
605 let mut ws = WS::default();
606 let mut file = None;
607
608 for p in pair.into_inner() {
609 match p.as_rule() {
610 Rule::tag_start => {
611 ws.left = p.as_span().as_str() == "{%-";
612 }
613 Rule::string => file = Some(replace_string_markers(p.as_span().as_str())),
614 Rule::tag_end => {
615 ws.right = p.as_span().as_str() == "-%}";
616 }
617 _ => unreachable!(),
618 };
619 }
620
621 Node::Extends(ws, file.unwrap())
622}
623
624fn parse_include(pair: Pair<Rule>) -> Node {
625 let mut ws = WS::default();
626 let mut files = vec![];
627 let mut ignore_missing = false;
628
629 for p in pair.into_inner() {
630 match p.as_rule() {
631 Rule::tag_start => {
632 ws.left = p.as_span().as_str() == "{%-";
633 }
634 Rule::string => {
635 files.push(replace_string_markers(p.as_span().as_str()));
636 }
637 Rule::string_array => files.extend(parse_string_array(p)),
638 Rule::ignore_missing => ignore_missing = true,
639 Rule::tag_end => {
640 ws.right = p.as_span().as_str() == "-%}";
641 }
642 _ => unreachable!(),
643 };
644 }
645
646 Node::Include(ws, files, ignore_missing)
647}
648
649fn parse_set_tag(pair: Pair<Rule>, global: bool) -> TeraResult<Node> {
650 let mut ws = WS::default();
651 let mut key = None;
652 let mut expr = None;
653
654 for p in pair.into_inner() {
655 match p.as_rule() {
656 Rule::tag_start => {
657 ws.left = p.as_span().as_str() == "{%-";
658 }
659 Rule::tag_end => {
660 ws.right = p.as_span().as_str() == "-%}";
661 }
662 Rule::ident => key = Some(p.as_str().to_string()),
663 Rule::logic_expr => expr = Some(parse_logic_expr(p)?),
664 Rule::array_filter => expr = Some(parse_array_with_filters(p)?),
665 _ => unreachable!("unexpected {:?} rule in parse_set_tag", p.as_rule()),
666 }
667 }
668
669 Ok(Node::Set(
670 ws,
671 Set {
672 key: key.unwrap(),
673 value: expr.unwrap(),
674 global,
675 },
676 ))
677}
678
679fn parse_raw_tag(pair: Pair<Rule>) -> Node {
680 let mut start_ws = WS::default();
681 let mut end_ws = WS::default();
682 let mut text = None;
683
684 for p in pair.into_inner() {
685 match p.as_rule() {
686 Rule::raw_tag => {
687 for p2 in p.into_inner() {
688 match p2.as_rule() {
689 Rule::tag_start => start_ws.left = p2.as_span().as_str() == "{%-",
690 Rule::tag_end => start_ws.right = p2.as_span().as_str() == "-%}",
691 _ => unreachable!(),
692 }
693 }
694 }
695 Rule::raw_text => text = Some(p.as_str().to_string()),
696 Rule::endraw_tag => {
697 for p2 in p.into_inner() {
698 match p2.as_rule() {
699 Rule::tag_start => end_ws.left = p2.as_span().as_str() == "{%-",
700 Rule::tag_end => end_ws.right = p2.as_span().as_str() == "-%}",
701 _ => unreachable!(),
702 }
703 }
704 }
705 _ => unreachable!("unexpected {:?} rule in parse_raw_tag", p.as_rule()),
706 };
707 }
708
709 Node::Raw(start_ws, text.unwrap(), end_ws)
710}
711
712fn parse_filter_section(pair: Pair<Rule>) -> TeraResult<Node> {
713 let mut start_ws = WS::default();
714 let mut end_ws = WS::default();
715 let mut filter = None;
716 let mut body = vec![];
717
718 for p in pair.into_inner() {
719 match p.as_rule() {
720 Rule::filter_tag => {
721 for p2 in p.into_inner() {
722 match p2.as_rule() {
723 Rule::tag_start => start_ws.left = p2.as_span().as_str() == "{%-",
724 Rule::tag_end => start_ws.right = p2.as_span().as_str() == "-%}",
725 Rule::fn_call => filter = Some(parse_fn_call(p2)?),
726 Rule::ident => {
727 filter = Some(FunctionCall {
728 name: p2.as_str().to_string(),
729 args: HashMap::new(),
730 });
731 }
732 _ => unreachable!("Got {:?} while parsing filter_tag", p2),
733 }
734 }
735 }
736 Rule::content
737 | Rule::macro_content
738 | Rule::block_content
739 | Rule::filter_section_content
740 | Rule::for_content => {
741 body.extend(parse_content(p)?);
742 }
743 Rule::endfilter_tag => {
744 for p2 in p.into_inner() {
745 match p2.as_rule() {
746 Rule::tag_start => end_ws.left = p2.as_span().as_str() == "{%-",
747 Rule::tag_end => end_ws.right = p2.as_span().as_str() == "-%}",
748 _ => unreachable!(),
749 }
750 }
751 }
752 _ => unreachable!("unexpected {:?} rule in parse_filter_section", p.as_rule()),
753 };
754 }
755 Ok(Node::FilterSection(
756 start_ws,
757 FilterSection {
758 filter: filter.unwrap(),
759 body,
760 },
761 end_ws,
762 ))
763}
764
765fn parse_block(pair: Pair<Rule>) -> TeraResult<Node> {
766 let mut start_ws = WS::default();
767 let mut end_ws = WS::default();
768 let mut name = None;
769 let mut body = vec![];
770
771 for p in pair.into_inner() {
772 match p.as_rule() {
773 Rule::block_tag => {
774 for p2 in p.into_inner() {
775 match p2.as_rule() {
776 Rule::tag_start => start_ws.left = p2.as_span().as_str() == "{%-",
777 Rule::tag_end => start_ws.right = p2.as_span().as_str() == "-%}",
778 Rule::ident => name = Some(p2.as_span().as_str().to_string()),
779 _ => unreachable!(),
780 };
781 }
782 }
783 Rule::block_content => body.extend(parse_content(p)?),
784 Rule::endblock_tag => {
785 for p2 in p.into_inner() {
786 match p2.as_rule() {
787 Rule::tag_start => end_ws.left = p2.as_span().as_str() == "{%-",
788 Rule::tag_end => end_ws.right = p2.as_span().as_str() == "-%}",
789 Rule::ident => (),
790 _ => unreachable!(),
791 };
792 }
793 }
794 _ => unreachable!("unexpected {:?} rule in parse_filter_section", p.as_rule()),
795 };
796 }
797
798 Ok(Node::Block(
799 start_ws,
800 Block {
801 name: name.unwrap(),
802 body,
803 },
804 end_ws,
805 ))
806}
807
808fn parse_macro_arg(p: Pair<Rule>) -> TeraResult<ExprVal> {
809 let val = match p.as_rule() {
810 Rule::int => Some(ExprVal::Int(p.as_str().parse().map_err(|_| {
811 Error::msg(format!("Integer out of bounds: `{}`", p.as_str()))
812 })?)),
813 Rule::float => Some(ExprVal::Float(p.as_str().parse().map_err(|_| {
814 Error::msg(format!("Float out of bounds: `{}`", p.as_str()))
815 })?)),
816 Rule::boolean => match p.as_str() {
817 "true" => Some(ExprVal::Bool(true)),
818 "True" => Some(ExprVal::Bool(true)),
819 "false" => Some(ExprVal::Bool(false)),
820 "False" => Some(ExprVal::Bool(false)),
821 _ => unreachable!(),
822 },
823 Rule::string => Some(ExprVal::String(replace_string_markers(p.as_str()))),
824 _ => unreachable!("Got {:?} in parse_macro_arg: {}", p.as_rule(), p.as_str()),
825 };
826
827 Ok(val.unwrap())
828}
829
830fn parse_macro_fn(pair: Pair<Rule>) -> TeraResult<(String, HashMap<String, Option<Expr>>)> {
831 let mut name = String::new();
832 let mut args = HashMap::new();
833
834 for p2 in pair.into_inner() {
835 match p2.as_rule() {
836 Rule::ident => name = p2.as_str().to_string(),
837 Rule::macro_def_arg => {
838 let mut arg_name = None;
839 let mut default_val = None;
840 for p3 in p2.into_inner() {
841 match p3.as_rule() {
842 Rule::ident => arg_name = Some(p3.as_str().to_string()),
843 _ => default_val = Some(Expr::new(parse_macro_arg(p3)?)),
844 };
845 }
846 args.insert(arg_name.unwrap(), default_val);
847 }
848 _ => continue,
849 }
850 }
851
852 Ok((name, args))
853}
854
855fn parse_macro_definition(pair: Pair<Rule>) -> TeraResult<Node> {
856 let mut start_ws = WS::default();
857 let mut end_ws = WS::default();
858 let mut name = String::new();
859 let mut args = HashMap::new();
860 let mut body = vec![];
861
862 for p in pair.into_inner() {
863 match p.as_rule() {
864 Rule::macro_tag => {
865 for p2 in p.into_inner() {
866 match p2.as_rule() {
867 Rule::tag_start => start_ws.left = p2.as_span().as_str() == "{%-",
868 Rule::tag_end => start_ws.right = p2.as_span().as_str() == "-%}",
869 Rule::macro_fn_wrapper => {
870 let macro_fn = parse_macro_fn(p2)?;
871 name = macro_fn.0;
872 args = macro_fn.1;
873 }
874 _ => continue,
875 };
876 }
877 }
878 Rule::macro_content => body.extend(parse_content(p)?),
879 Rule::endmacro_tag => {
880 for p2 in p.into_inner() {
881 match p2.as_rule() {
882 Rule::tag_start => end_ws.left = p2.as_span().as_str() == "{%-",
883 Rule::tag_end => end_ws.right = p2.as_span().as_str() == "-%}",
884 Rule::ident => (),
885 _ => unreachable!(),
886 };
887 }
888 }
889 _ => unreachable!(
890 "unexpected {:?} rule in parse_macro_definition",
891 p.as_rule()
892 ),
893 }
894 }
895
896 Ok(Node::MacroDefinition(
897 start_ws,
898 MacroDefinition { name, args, body },
899 end_ws,
900 ))
901}
902
903fn parse_forloop(pair: Pair<Rule>) -> TeraResult<Node> {
904 let mut start_ws = WS::default();
905 let mut end_ws = WS::default();
906
907 let mut key = None;
908 let mut value = None;
909 let mut container = None;
910 let mut body = vec![];
911 let mut empty_body: Option<Vec<Node>> = None;
912
913 for p in pair.into_inner() {
914 match p.as_rule() {
915 Rule::for_tag => {
916 let mut idents = vec![];
917 for p2 in p.into_inner() {
918 match p2.as_rule() {
919 Rule::tag_start => start_ws.left = p2.as_span().as_str() == "{%-",
920 Rule::tag_end => start_ws.right = p2.as_span().as_str() == "-%}",
921 Rule::ident => idents.push(p2.as_str().to_string()),
922 Rule::basic_expr_filter => {
923 container = Some(parse_basic_expr_with_filters(p2)?);
924 }
925 Rule::array_filter => container = Some(parse_array_with_filters(p2)?),
926 _ => unreachable!(),
927 };
928 }
929
930 if idents.len() == 1 {
931 value = Some(idents[0].clone());
932 } else {
933 key = Some(idents[0].clone());
934 value = Some(idents[1].clone());
935 }
936 }
937 Rule::content
938 | Rule::macro_content
939 | Rule::block_content
940 | Rule::filter_section_content
941 | Rule::for_content => {
942 match empty_body {
943 Some(ref mut empty_body) => empty_body.extend(parse_content(p)?),
944 None => body.extend(parse_content(p)?),
945 };
946 }
947 Rule::else_tag => {
948 empty_body = Some(vec![]);
949 }
950 Rule::endfor_tag => {
951 for p2 in p.into_inner() {
952 match p2.as_rule() {
953 Rule::tag_start => end_ws.left = p2.as_span().as_str() == "{%-",
954 Rule::tag_end => end_ws.right = p2.as_span().as_str() == "-%}",
955 Rule::ident => (),
956 _ => unreachable!(),
957 };
958 }
959 }
960 _ => unreachable!("unexpected {:?} rule in parse_forloop", p.as_rule()),
961 };
962 }
963
964 Ok(Node::Forloop(
965 start_ws,
966 Forloop {
967 key,
968 value: value.unwrap(),
969 container: container.unwrap(),
970 body,
971 empty_body,
972 },
973 end_ws,
974 ))
975}
976
977fn parse_break_tag(pair: Pair<Rule>) -> Node {
978 let mut ws = WS::default();
979
980 for p in pair.into_inner() {
981 match p.as_rule() {
982 Rule::tag_start => {
983 ws.left = p.as_span().as_str() == "{%-";
984 }
985 Rule::tag_end => {
986 ws.right = p.as_span().as_str() == "-%}";
987 }
988 _ => unreachable!(),
989 };
990 }
991
992 Node::Break(ws)
993}
994
995fn parse_continue_tag(pair: Pair<Rule>) -> Node {
996 let mut ws = WS::default();
997
998 for p in pair.into_inner() {
999 match p.as_rule() {
1000 Rule::tag_start => {
1001 ws.left = p.as_span().as_str() == "{%-";
1002 }
1003 Rule::tag_end => {
1004 ws.right = p.as_span().as_str() == "-%}";
1005 }
1006 _ => unreachable!(),
1007 };
1008 }
1009
1010 Node::Continue(ws)
1011}
1012
1013fn parse_comment_tag(pair: Pair<Rule>) -> Node {
1014 let mut ws = WS::default();
1015 let mut content = String::new();
1016
1017 for p in pair.into_inner() {
1018 match p.as_rule() {
1019 Rule::comment_start => {
1020 ws.left = p.as_span().as_str() == "{#-";
1021 }
1022 Rule::comment_end => {
1023 ws.right = p.as_span().as_str() == "-#}";
1024 }
1025 Rule::comment_text => {
1026 content = p.as_str().to_owned();
1027 }
1028 _ => unreachable!(),
1029 };
1030 }
1031
1032 Node::Comment(ws, content)
1033}
1034
1035fn parse_if(pair: Pair<Rule>) -> TeraResult<Node> {
1036 let mut end_ws = WS::default();
1038 let mut conditions = vec![];
1039 let mut otherwise = None;
1040
1041 let mut current_ws = WS::default();
1043 let mut expr = None;
1044 let mut current_body = vec![];
1045 let mut in_else = false;
1046
1047 for p in pair.into_inner() {
1048 match p.as_rule() {
1049 Rule::if_tag | Rule::elif_tag => {
1050 if p.as_rule() == Rule::elif_tag {
1052 conditions.push((current_ws, expr.unwrap(), current_body));
1053 expr = None;
1054 current_ws = WS::default();
1055 current_body = vec![];
1056 }
1057
1058 for p2 in p.into_inner() {
1059 match p2.as_rule() {
1060 Rule::tag_start => current_ws.left = p2.as_span().as_str() == "{%-",
1061 Rule::tag_end => current_ws.right = p2.as_span().as_str() == "-%}",
1062 Rule::logic_expr => expr = Some(parse_logic_expr(p2)?),
1063 _ => unreachable!(),
1064 };
1065 }
1066 }
1067 Rule::content
1068 | Rule::macro_content
1069 | Rule::block_content
1070 | Rule::for_content
1071 | Rule::filter_section_content => current_body.extend(parse_content(p)?),
1072 Rule::else_tag => {
1073 if expr.is_some() {
1075 conditions.push((current_ws, expr.unwrap(), current_body));
1076 expr = None;
1077 current_ws = WS::default();
1078 current_body = vec![];
1079 }
1080 in_else = true;
1081 for p2 in p.into_inner() {
1082 match p2.as_rule() {
1083 Rule::tag_start => current_ws.left = p2.as_span().as_str() == "{%-",
1084 Rule::tag_end => current_ws.right = p2.as_span().as_str() == "-%}",
1085 _ => unreachable!(),
1086 };
1087 }
1088 }
1089 Rule::endif_tag => {
1090 if in_else {
1091 otherwise = Some((current_ws, current_body));
1092 } else {
1093 conditions.push((current_ws, expr.unwrap(), current_body));
1095 }
1096
1097 for p2 in p.into_inner() {
1098 match p2.as_rule() {
1099 Rule::tag_start => end_ws.left = p2.as_span().as_str() == "{%-",
1100 Rule::tag_end => end_ws.right = p2.as_span().as_str() == "-%}",
1101 _ => unreachable!(),
1102 };
1103 }
1104 break;
1105 }
1106 _ => unreachable!("unreachable rule in parse_if: {:?}", p.as_rule()),
1107 }
1108 }
1109
1110 Ok(Node::If(
1111 If {
1112 conditions,
1113 otherwise,
1114 },
1115 end_ws,
1116 ))
1117}
1118
1119fn parse_content(pair: Pair<Rule>) -> TeraResult<Vec<Node>> {
1120 let mut nodes = vec![];
1121
1122 for p in pair.into_inner() {
1123 match p.as_rule() {
1124 Rule::include_tag => nodes.push(parse_include(p)),
1125 Rule::comment_tag => nodes.push(parse_comment_tag(p)),
1126 Rule::super_tag => nodes.push(Node::Super),
1127 Rule::set_tag => nodes.push(parse_set_tag(p, false)?),
1128 Rule::set_global_tag => nodes.push(parse_set_tag(p, true)?),
1129 Rule::raw => nodes.push(parse_raw_tag(p)),
1130 Rule::variable_tag => nodes.push(parse_variable_tag(p)?),
1131 Rule::macro_definition => nodes.push(parse_macro_definition(p)?),
1132 Rule::forloop => nodes.push(parse_forloop(p)?),
1133 Rule::break_tag => nodes.push(parse_break_tag(p)),
1134 Rule::continue_tag => nodes.push(parse_continue_tag(p)),
1135 Rule::content_if
1136 | Rule::macro_if
1137 | Rule::block_if
1138 | Rule::for_if
1139 | Rule::filter_section_if => nodes.push(parse_if(p)?),
1140 Rule::filter_section => nodes.push(parse_filter_section(p)?),
1141 Rule::text => nodes.push(Node::Text(p.as_span().as_str().to_string())),
1142 Rule::block => nodes.push(parse_block(p)?),
1143 _ => unreachable!("unreachable content rule: {:?}", p.as_rule()),
1144 };
1145 }
1146
1147 Ok(nodes)
1148}
1149
1150pub fn parse(input: &str) -> TeraResult<Vec<Node>> {
1151 let mut pairs = match TeraParser::parse(Rule::template, input) {
1152 Ok(p) => p,
1153 Err(e) => {
1154 let fancy_e = e.renamed_rules(|rule| {
1155 match *rule {
1156 Rule::EOI => "end of input".to_string(),
1157 Rule::int => "an integer".to_string(),
1158 Rule::float => "a float".to_string(),
1159 Rule::string
1160 | Rule::double_quoted_string
1161 | Rule::single_quoted_string
1162 | Rule::backquoted_quoted_string => {
1163 "a string".to_string()
1164 }
1165 Rule::string_concat => "a concatenation of strings".to_string(),
1166 Rule::string_expr_filter => "a string or a concatenation of strings".to_string(),
1167 Rule::all_chars => "a character".to_string(),
1168 Rule::array => "an array of values".to_string(),
1169 Rule::array_filter => "an array of values with an optional filter".to_string(),
1170 Rule::string_array => "an array of strings".to_string(),
1171 Rule::basic_val => "a value".to_string(),
1172 Rule::basic_op => "a mathematical operator".to_string(),
1173 Rule::comparison_op => "a comparison operator".to_string(),
1174 Rule::boolean => "`true` or `false`".to_string(),
1175 Rule::ident => "an identifier (must start with a-z)".to_string(),
1176 Rule::dotted_ident => "a dotted identifier (identifiers separated by `.`)".to_string(),
1177 Rule::dotted_square_bracket_ident => "a square bracketed identifier (identifiers separated by `.` or `[]`s)".to_string(),
1178 Rule::square_brackets => "an identifier, string or integer inside `[]`s".to_string(),
1179 Rule::basic_expr_filter => "an expression with an optional filter".to_string(),
1180 Rule::comparison_val => "a comparison value".to_string(),
1181 Rule::basic_expr | Rule::comparison_expr => "an expression".to_string(),
1182 Rule::logic_val => "a value that can be negated".to_string(),
1183 Rule::logic_expr => "any expressions".to_string(),
1184 Rule::fn_call => "a function call".to_string(),
1185 Rule::kwarg => "a keyword argument: `key=value` where `value` can be any expressions".to_string(),
1186 Rule::kwargs => "a list of keyword arguments: `key=value` where `value` can be any expressions and separated by `,`".to_string(),
1187 Rule::op_or => "`or`".to_string(),
1188 Rule::op_and => "`and`".to_string(),
1189 Rule::op_not => "`not`".to_string(),
1190 Rule::op_lte => "`<=`".to_string(),
1191 Rule::op_gte => "`>=`".to_string(),
1192 Rule::op_lt => "`<`".to_string(),
1193 Rule::op_gt => "`>`".to_string(),
1194 Rule::op_ineq => "`!=`".to_string(),
1195 Rule::op_eq => "`==`".to_string(),
1196 Rule::op_plus => "`+`".to_string(),
1197 Rule::op_minus => "`-`".to_string(),
1198 Rule::op_times => "`*`".to_string(),
1199 Rule::op_slash => "`/`".to_string(),
1200 Rule::op_modulo => "`%`".to_string(),
1201 Rule::filter => "a filter".to_string(),
1202 Rule::test => "a test".to_string(),
1203 Rule::test_not => "a negated test".to_string(),
1204 Rule::test_call => "a test call".to_string(),
1205 Rule::test_arg => "a test argument (any expressions including arrays)".to_string(),
1206 Rule::test_args => "a list of test arguments (any expression including arrayss)".to_string(),
1207 Rule::macro_fn | Rule::macro_fn_wrapper => "a macro function".to_string(),
1208 Rule::macro_call => "a macro function call".to_string(),
1209 Rule::macro_def_arg => {
1210 "an argument name with an optional default literal value: `id`, `key=1`".to_string()
1211 }
1212 Rule::macro_def_args => {
1213 "a list of argument names with an optional default literal value: `id`, `key=1`".to_string()
1214 }
1215 Rule::endmacro_tag => "`{% endmacro %}`".to_string(),
1216 Rule::macro_content => "the macro content".to_string(),
1217 Rule::filter_section_content => "the filter section content".to_string(),
1218 Rule::set_tag => "a `set` tag`".to_string(),
1219 Rule::set_global_tag => "a `set_global` tag`".to_string(),
1220 Rule::block_content | Rule::content | Rule::for_content => {
1221 "some content".to_string()
1222 },
1223 Rule::text => "some text".to_string(),
1224 Rule::tag_start => "tag".to_string(),
1228 Rule::tag_end => "`%}` or `-%}`".to_string(),
1229 Rule::super_tag => "`{{ super() }}`".to_string(),
1230 Rule::raw_tag => "`{% raw %}`".to_string(),
1231 Rule::raw_text => "some raw text".to_string(),
1232 Rule::raw => "a raw block (`{% raw %}...{% endraw %}`".to_string(),
1233 Rule::endraw_tag => "`{% endraw %}`".to_string(),
1234 Rule::ignore_missing => "ignore missing mark for include tag".to_string(),
1235 Rule::include_tag => r#"an include tag (`{% include "..." %}`)"#.to_string(),
1236 Rule::comment_tag => "a comment tag (`{#...#}`)".to_string(),
1237 Rule::comment_text => "the context of a comment (`{# ... #}`)".to_string(),
1238 Rule::variable_tag => "a variable tag (`{{ ... }}`)".to_string(),
1239 Rule::filter_tag | Rule::filter_section => {
1240 "a filter section (`{% filter something %}...{% endfilter %}`)".to_string()
1241 }
1242 Rule::for_tag | Rule::forloop => {
1243 "a forloop (`{% for i in something %}...{% endfor %}".to_string()
1244 },
1245 Rule::endfilter_tag => "an endfilter tag (`{% endfilter %}`)".to_string(),
1246 Rule::endfor_tag => "an endfor tag (`{% endfor %}`)".to_string(),
1247 Rule::if_tag
1248 | Rule::content_if
1249 | Rule::block_if
1250 | Rule::macro_if
1251 | Rule::for_if
1252 | Rule::filter_section_if => {
1253 "a `if` tag".to_string()
1254 }
1255 Rule::elif_tag => "an `elif` tag".to_string(),
1256 Rule::else_tag => "an `else` tag".to_string(),
1257 Rule::endif_tag => "an endif tag (`{% endif %}`)".to_string(),
1258 Rule::WHITESPACE => "whitespace".to_string(),
1259 Rule::variable_start => "a variable start (`{{`)".to_string(),
1260 Rule::variable_end => "a variable end (`}}`)".to_string(),
1261 Rule::comment_start => "a comment start (`{#`)".to_string(),
1262 Rule::comment_end => "a comment end (`#}`)".to_string(),
1263 Rule::block_start => "`{{`, `{%` or `{#`".to_string(),
1264 Rule::import_macro_tag => r#"an import macro tag (`{% import "filename" as namespace %}`"#.to_string(),
1265 Rule::block | Rule::block_tag => r#"a block tag (`{% block block_name %}`"#.to_string(),
1266 Rule::endblock_tag => r#"an endblock tag (`{% endblock block_name %}`"#.to_string(),
1267 Rule::macro_definition
1268 | Rule::macro_tag => r#"a macro definition tag (`{% macro my_macro() %}`"#.to_string(),
1269 Rule::extends_tag => r#"an extends tag (`{% extends "myfile" %}`"#.to_string(),
1270 Rule::template => "a template".to_string(),
1271 Rule::break_tag => "a break tag".to_string(),
1272 Rule::continue_tag => "a continue tag".to_string(),
1273 Rule::top_imports => "top imports".to_string(),
1274 Rule::in_cond => "a `in` condition".to_string(),
1275 Rule::in_cond_container => "a `in` condition container: a string, an array or an ident".to_string(),
1276 }
1277 });
1278 return Err(Error::msg(fancy_e));
1279 }
1280 };
1281
1282 let mut nodes = vec![];
1283
1284 for p in pairs.next().unwrap().into_inner() {
1286 match p.as_rule() {
1287 Rule::extends_tag => nodes.push(parse_extends(p)),
1288 Rule::import_macro_tag => nodes.push(parse_import_macro(p)),
1289 Rule::content => nodes.extend(parse_content(p)?),
1290 Rule::comment_tag => (),
1291 Rule::EOI => (),
1292 _ => unreachable!("unknown tpl rule: {:?}", p.as_rule()),
1293 }
1294 }
1295
1296 Ok(nodes)
1297}