runjucks_core/parser/mod.rs
1//! Builds [`crate::ast::Node`] trees from [`crate::lexer::Token`] streams.
2//!
3//! `{{ … }}` bodies are parsed with [`parse_expr`] (nom-driven, Nunjucks-style precedence; see [`expr`]).
4//! `{% … %}` supports `if` / `elif` / `else` / `endif`, `switch` / `case` / `default` / `endswitch`, `for` / `else` / `endfor` (multi-var / tuple unpack), `set` (incl. `endset` capture), `include` (expression + `ignore missing` + `with`/`without context`), `import` / `from` (macro libraries), `extends` (expression), `block` / `endblock`, `macro` / `endmacro` (defaults + call kwargs), `filter` / `endfilter`, `call` / `endcall`, and `raw` / `endraw` / `verbatim` / `endverbatim` (see [`template`]).
5//! For unimplemented tags, use [`crate::tag_lex::tokenize_tag_body`] for tokenization only.
6
7pub mod expr;
8mod split;
9mod template;
10
11use crate::ast::{Expr, Node};
12use crate::errors::Result;
13use crate::extension::ExtensionTagMeta;
14use crate::lexer::Token;
15use std::collections::{HashMap, HashSet};
16
17/// Parses a token stream into a single [`Node::Root`] containing child nodes.
18///
19/// # Errors
20///
21/// Returns an error on malformed `{% %}` blocks, unknown tag keywords, or invalid expressions.
22///
23/// # Examples
24///
25/// ```
26/// use runjucks_core::lexer::tokenize;
27/// use runjucks_core::parser::parse;
28///
29/// let tokens = tokenize("a{{x}}b").unwrap();
30/// let root = parse(&tokens).unwrap();
31/// ```
32pub fn parse(tokens: &[Token]) -> Result<Node> {
33 template::parse_template_tokens(tokens, &HashMap::new(), &HashSet::new())
34}
35
36/// Parse with custom extension tags registered on the [`crate::Environment`].
37pub fn parse_with_env(
38 tokens: &[Token],
39 extension_tags: &HashMap<String, ExtensionTagMeta>,
40 extension_closing: &HashSet<String>,
41) -> Result<Node> {
42 template::parse_template_tokens(tokens, extension_tags, extension_closing)
43}
44
45pub(crate) use template::is_reserved_tag_keyword;
46
47/// Parses the inside of a `{{` … `}}` region into an [`Expr`].
48///
49/// # Errors
50///
51/// Invalid syntax or trailing garbage (after trim) yields [`RunjucksError`].
52///
53/// # Examples
54///
55/// ```
56/// use runjucks_core::parser::parse_expr;
57/// use runjucks_core::ast::Expr;
58///
59/// match parse_expr("foo").unwrap() {
60/// Expr::Variable(name) => assert_eq!(name, "foo"),
61/// _ => panic!("expected variable"),
62/// }
63/// ```
64pub fn parse_expr(source: &str) -> Result<Expr> {
65 expr::parse_expression(source)
66}