1 WHITESPACE = _{ " " | "\t" | "\r" | "\n" }
4 int = @{ "-" ? ~ ("0" | '1'..'9' ~ '0'..'9' * ) }
8 "0" ~ "." ~ '0'..'9' + |
9 '1'..'9' ~ '0'..'9' * ~ "." ~ '0'..'9' +
12 // matches anything between 2 double quotes
13 double_quoted_string = @{ "\"" ~ (!("\"") ~ ANY)* ~ "\""}
14 // matches anything between 2 single quotes
15 single_quoted_string = @{ "\'" ~ (!("\'") ~ ANY)* ~ "\'"}
16 // matches anything between 2 backquotes\backticks
17 backquoted_quoted_string = @{ "`" ~ (!("`") ~ ANY)* ~ "`"}
20 double_quoted_string |
21 single_quoted_string |
22 backquoted_quoted_string
25 boolean = { "true" | "false" | "True" | "False" }
27 // -----------------------------------------------
30 op_or = @{ "or" ~ WHITESPACE }
31 op_and = @{ "and" ~ WHITESPACE }
32 op_not = @{ "not" ~ WHITESPACE }
45 // -------------------------------------------------
49 all_chars = _{'a'..'z' | 'A'..'Z' | "_" | '0'..'9'}
50 // Used everywhere where an ident is used, except when accessing
51 // data from the context.
52 // Eg block name, argument name, macro name etc
54 ('a'..'z' | 'A'..'Z' | "_") ~
58 // The context_ident used to get data from the context.
59 // Same as ident but allows `.` in it
61 ('a'..'z' | 'A'..'Z' | "_") ~
67 "[" ~ (int | string | dotted_square_bracket_ident) ~ "]"
70 dotted_square_bracket_ident = @{
71 dotted_ident ~ ( ("." ~ all_chars+) | square_brackets )*
74 string_concat = { (fn_call | float | int | string | dotted_square_bracket_ident) ~ ("~" ~ (fn_call | float | int | string | dotted_square_bracket_ident))+ }
76 // ----------------------------------------------------
79 /// We'll use precedence climbing on those in the parser phase
81 // boolean first so they are not caught as identifiers
82 basic_val = _{ boolean | test_not | test | macro_call | fn_call | dotted_square_bracket_ident | float | int }
83 basic_op = _{ op_plus | op_minus | op_times | op_slash | op_modulo }
84 basic_expr = { ("(" ~ basic_expr ~ ")" | basic_val) ~ (basic_op ~ basic_val)* }
85 basic_expr_filter = !{ basic_expr ~ filter* }
86 string_expr_filter = !{ (string_concat | string) ~ filter* }
88 comparison_val = { basic_expr_filter ~ (basic_op ~ basic_expr_filter)* }
89 comparison_op = _{ op_lte | op_gte | op_gt | op_lt | op_eq | op_ineq }
90 comparison_expr = { (string_expr_filter | comparison_val) ~ (comparison_op ~ (string_expr_filter | comparison_val))? }
93 in_cond_container = {string_expr_filter | array_filter | dotted_square_bracket_ident}
94 in_cond = !{ (string_expr_filter | basic_expr_filter) ~ op_not? ~ "in" ~ in_cond_container }
96 logic_val = !{ op_not? ~ (in_cond | comparison_expr) }
97 logic_expr = !{ logic_val ~ ((op_or | op_and) ~ logic_val)* }
99 array = !{ "[" ~ (logic_val ~ ",")* ~ logic_val? ~ "]"}
100 array_filter = !{ array ~ filter* }
102 string_array = !{ "[" ~ (string ~ ",")* ~ string? ~ "]"}
104 // ----------------------------------------------------
106 /// FUNCTIONS & FILTERS
108 // A keyword argument: something=10, something="a value", something=1+10 etc
109 kwarg = { ident ~ "=" ~ (logic_expr | array_filter) }
110 kwargs = _{ kwarg ~ ("," ~ kwarg )* ~ ","? }
111 fn_call = !{ ident ~ "(" ~ kwargs? ~ ")" }
112 filter = { "|" ~ (fn_call | ident) }
115 // ------------------------------------------------------
119 // A macro argument can have default value, only a literal though
120 macro_def_arg = ${ (ident ~ "=" ~ (boolean | string | float | int)) | ident }
121 macro_def_args = _{ macro_def_arg ~ ("," ~ macro_def_arg)* }
122 macro_fn = _{ ident ~ "(" ~ macro_def_args? ~ ")" }
123 macro_fn_wrapper = !{ macro_fn }
124 macro_call = { ident ~ "::" ~ ident ~ "(" ~ kwargs? ~ ")" }
127 // -------------------------------------------------------
131 // It's a bit weird that tests are the only thing in Tera not using kwargs
132 // but at the same time it's one arg most of the time so...
133 test_arg = { logic_expr | array_filter }
134 test_args = _{ test_arg ~ ("," ~ test_arg)* }
135 test_call = !{ ident ~ ("(" ~ test_args ~ ")")? }
136 test_not = { dotted_ident ~ "is" ~ "not" ~ test_call }
137 test = { dotted_ident ~ "is" ~ test_call }
139 // -------------------------------------------------------
143 // All the blocks that Tera recognises
144 variable_start = { "{{-" | "{{" }
145 variable_end = { "-}}" | "}}" }
146 // whitespace control
147 tag_start = { "{%-" | "{%" }
148 tag_end = { "-%}" | "%}" }
149 comment_start = { "{#-" | "{#" }
150 comment_end = { "-#}" | "#}" }
151 block_start = _{ variable_start | tag_start | comment_start }
153 comment_text = ${ (!(comment_end) ~ ANY)+ }
156 ignore_missing = { "ignore" ~ WHITESPACE* ~ "missing" }
160 include_tag = ${ tag_start ~ WHITESPACE* ~ "include" ~ WHITESPACE+ ~ (string | string_array) ~ WHITESPACE* ~ ignore_missing? ~ WHITESPACE* ~ tag_end }
161 comment_tag = ${ comment_start ~ comment_text ~ comment_end }
162 block_tag = ${ tag_start ~ WHITESPACE* ~ "block" ~ WHITESPACE+ ~ ident ~ WHITESPACE* ~ tag_end }
163 macro_tag = ${ tag_start ~ WHITESPACE* ~ "macro" ~ WHITESPACE+ ~ macro_fn_wrapper ~ WHITESPACE* ~ tag_end }
164 if_tag = ${ tag_start ~ WHITESPACE* ~ "if" ~ WHITESPACE+ ~ logic_expr ~ WHITESPACE* ~ tag_end }
165 elif_tag = ${ tag_start ~ WHITESPACE* ~ "elif" ~ WHITESPACE+ ~ logic_expr ~ WHITESPACE* ~ tag_end }
166 else_tag = !{ tag_start ~ "else" ~ tag_end }
168 tag_start ~ WHITESPACE*
169 ~ "for"~ WHITESPACE+ ~ ident ~ ("," ~ WHITESPACE* ~ ident)? ~ WHITESPACE+ ~ "in" ~ WHITESPACE+ ~ (basic_expr_filter | array_filter)
170 ~ WHITESPACE* ~ tag_end
173 tag_start ~ WHITESPACE*
174 ~ "filter" ~ WHITESPACE+ ~ (fn_call | ident)
175 ~ WHITESPACE* ~ tag_end
178 tag_start ~ WHITESPACE*
179 ~ "set" ~ WHITESPACE+ ~ ident ~ WHITESPACE* ~ "=" ~ WHITESPACE* ~ (logic_expr | array_filter)
180 ~ WHITESPACE* ~ tag_end
183 tag_start ~ WHITESPACE*
184 ~ "set_global" ~ WHITESPACE+ ~ ident ~ WHITESPACE* ~ "=" ~ WHITESPACE* ~ (logic_expr | array_filter)
185 ~ WHITESPACE* ~ tag_end
187 endblock_tag = !{ tag_start ~ "endblock" ~ ident? ~ tag_end }
188 endmacro_tag = !{ tag_start ~ "endmacro" ~ ident? ~ tag_end }
189 endif_tag = !{ tag_start ~ "endif" ~ tag_end }
190 endfor_tag = !{ tag_start ~ "endfor" ~ tag_end }
191 endfilter_tag = !{ tag_start ~ "endfilter" ~ tag_end }
192 break_tag = !{ tag_start ~ "break" ~ tag_end }
193 continue_tag = !{ tag_start ~ "continue" ~ tag_end }
195 variable_tag = !{ variable_start ~ (logic_expr | array_filter) ~ variable_end }
196 super_tag = !{ variable_start ~ "super()" ~ variable_end }
198 text = ${ (!(block_start) ~ ANY)+ }
200 raw_tag = !{ tag_start ~ "raw" ~ tag_end }
201 endraw_tag = !{ tag_start ~ "endraw" ~ tag_end }
202 raw_text = ${ (!endraw_tag ~ ANY)* }
203 raw = ${ raw_tag ~ raw_text ~ endraw_tag }
205 filter_section = ${ filter_tag ~ filter_section_content* ~ endfilter_tag }
207 forloop = ${ for_tag ~ for_content* ~ (else_tag ~ for_content*)* ~ endfor_tag }
209 macro_if = ${ if_tag ~ macro_content* ~ (elif_tag ~ macro_content*)* ~ (else_tag ~ macro_content*)? ~ endif_tag }
210 block_if = ${ if_tag ~ block_content* ~ (elif_tag ~ block_content*)* ~ (else_tag ~ block_content*)? ~ endif_tag }
211 for_if = ${ if_tag ~ for_content* ~ (elif_tag ~ for_content*)* ~ (else_tag ~ for_content*)? ~ endif_tag }
212 filter_section_if = ${ if_tag ~ filter_section_content* ~ (elif_tag ~ filter_section_content*)* ~ (else_tag ~ filter_section_content*)? ~ endif_tag }
213 content_if = ${ if_tag ~ content* ~ (elif_tag ~ content*)* ~ (else_tag ~ content*)? ~ endif_tag }
215 block = ${ block_tag ~ block_content* ~ endblock_tag }
216 macro_definition = ${ macro_tag ~ macro_content* ~ endmacro_tag }
218 filter_section_content = @{
232 // smaller sets of allowed content in macros
246 // smaller set of allowed content in block
262 // set of allowed content inside for loops
294 WHITESPACE* ~ tag_start ~ WHITESPACE*
295 ~ "extends" ~ WHITESPACE+ ~ string
296 ~ WHITESPACE* ~ tag_end ~ WHITESPACE*
298 import_macro_tag = ${
299 WHITESPACE* ~ tag_start ~ WHITESPACE*
300 ~ "import" ~ WHITESPACE+ ~ string ~ WHITESPACE+ ~ "as" ~ WHITESPACE+ ~ ident
301 ~ WHITESPACE* ~ tag_end ~ WHITESPACE*
304 (extends_tag ~ import_macro_tag*)
306 (import_macro_tag+ ~ extends_tag?)