1WHITESPACE = _{ " "|"\t"|"\n"|"\r" }
2keywords = { "as" | "else" }
3
4escape = @{ ("\\" ~ "{{" ~ "{{"?) | ("\\" ~ "\\"+ ~ &"{{") }
5raw_text = ${ ( escape | (!"{{" ~ ANY) )+ }
6raw_block_text = ${ ( escape | (!"{{{{" ~ ANY) )* }
7
8literal = { string_literal |
9            array_literal |
10            object_literal |
11            number_literal |
12            null_literal |
13            boolean_literal }
14
15null_literal = @{ "null" ~ !symbol_char }
16boolean_literal = @{ ("true"|"false") ~ !symbol_char }
17number_literal = @{ "-"? ~ ASCII_DIGIT+ ~ "."? ~ ASCII_DIGIT* ~ ("E" ~ "-"? ~ ASCII_DIGIT+)? }
18json_char_double_quote = {
19    !("\"" | "\\") ~ ANY
20    | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t")
21    | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4})
22}
23json_char_single_quote = {
24    !("'" | "\\") ~ ANY
25    | "\\" ~ ("'" | "\\" | "/" | "b" | "f" | "n" | "r" | "t")
26    | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4})
27}
28string_inner_double_quote = @{ json_char_double_quote* }
29string_inner_single_quote = @{ json_char_single_quote* }
30string_literal = ${ ("\"" ~ string_inner_double_quote ~ "\"") | ("'" ~ string_inner_single_quote ~ "'") }
31array_literal = { "[" ~ literal? ~ ("," ~ literal)* ~ "]" }
32object_literal = { "{" ~ (string_literal ~ ":" ~ literal)?
33                   ~ ("," ~ string_literal ~ ":" ~ literal)* ~ "}" }
34
35symbol_char = _{ASCII_ALPHANUMERIC|"-"|"_"|"$"|'\u{80}'..'\u{7ff}'|'\u{800}'..'\u{ffff}'|'\u{10000}'..'\u{10ffff}'}
36partial_symbol_char = _{ASCII_ALPHANUMERIC|"-"|"_"|'\u{80}'..'\u{7ff}'|'\u{800}'..'\u{ffff}'|'\u{10000}'..'\u{10ffff}'|"/"|"."}
37path_char = _{ "/" }
38
39identifier = @{ symbol_char+ }
40partial_identifier = @{ partial_symbol_char+ | ("[" ~ ANY+ ~ "]") | ("'" ~ (!"'" ~ ("\\'" | ANY))+ ~ "'") }
41reference = ${ path_inline }
42
43name = _{ subexpression | reference }
44
45param = { !(keywords ~ !symbol_char) ~ (literal | reference | subexpression) }
46hash = { identifier ~ "=" ~ param }
47block_param = { "as" ~ "|" ~ identifier ~ identifier? ~ "|"}
48exp_line = _{ identifier ~ (hash|param)* ~ block_param?}
49partial_exp_line = _{ ((partial_identifier|name) ~ (hash|param)*) }
50
51subexpression = { "(" ~ ((identifier ~ (hash|param)+) | reference)  ~ ")" }
52
53pre_whitespace_omitter = { "~" }
54pro_whitespace_omitter = { "~" }
55
56expression = { !invert_tag ~ "{{" ~ pre_whitespace_omitter? ~
57              ((identifier ~ (hash|param)+) | name )
58              ~ pro_whitespace_omitter? ~ "}}" }
59html_expression_triple_bracket = _{ "{{{" ~ pre_whitespace_omitter? ~
60                                    ((identifier ~ (hash|param)+) | name ) ~
61                                    pro_whitespace_omitter? ~ "}}}" }
62amp_expression = _{ "{{" ~ pre_whitespace_omitter? ~ "&" ~ name ~
63                       pro_whitespace_omitter? ~ "}}" }
64html_expression = { html_expression_triple_bracket | amp_expression }
65
66decorator_expression = { "{{" ~ pre_whitespace_omitter? ~ "*" ~ exp_line ~
67pro_whitespace_omitter? ~ "}}" }
68partial_expression = { "{{" ~ pre_whitespace_omitter? ~ ">" ~ partial_exp_line
69                     ~ pro_whitespace_omitter? ~ "}}" }
70
71invert_tag_item = { "else"|"^" }
72invert_tag = { !escape ~ "{{" ~ pre_whitespace_omitter? ~ invert_tag_item
73             ~ pro_whitespace_omitter? ~ "}}" }
74helper_block_start = { "{{" ~ pre_whitespace_omitter? ~ "#" ~ exp_line ~
75                     pro_whitespace_omitter? ~ "}}" }
76helper_block_end = { "{{" ~ pre_whitespace_omitter? ~ "/" ~ identifier ~
77                   pro_whitespace_omitter? ~ "}}" }
78helper_block = _{ helper_block_start ~ template ~
79                  (invert_tag ~ template)? ~ helper_block_end }
80
81decorator_block_start = { "{{" ~ pre_whitespace_omitter? ~ "#" ~ "*"
82                        ~ exp_line ~ pro_whitespace_omitter? ~ "}}" }
83decorator_block_end = { "{{" ~ pre_whitespace_omitter? ~ "/" ~ identifier ~
84                        pro_whitespace_omitter? ~ "}}" }
85decorator_block = _{ decorator_block_start ~ template ~
86                     decorator_block_end }
87
88partial_block_start = { "{{" ~ pre_whitespace_omitter? ~ "#" ~ ">"
89                        ~ partial_exp_line ~ pro_whitespace_omitter? ~ "}}" }
90partial_block_end = { "{{" ~ pre_whitespace_omitter? ~ "/" ~ partial_identifier ~
91                      pro_whitespace_omitter? ~ "}}" }
92partial_block = _{ partial_block_start ~ template ~ partial_block_end }
93
94raw_block_start = { "{{{{" ~ pre_whitespace_omitter? ~ exp_line ~
95                    pro_whitespace_omitter? ~ "}}}}" }
96raw_block_end = { "{{{{" ~ pre_whitespace_omitter? ~ "/" ~ identifier ~
97                  pro_whitespace_omitter? ~ "}}}}" }
98raw_block = _{ raw_block_start ~ raw_block_text ~ raw_block_end }
99
100hbs_comment = { "{{!" ~ "--" ~ (!"--}}" ~ ANY)* ~ "--" ~ "}}" }
101hbs_comment_compact = { "{{!" ~ (!"}}" ~ ANY)* ~ "}}" }
102
103template = { (
104            raw_text |
105            expression |
106            html_expression |
107            helper_block |
108            raw_block |
109            hbs_comment |
110            hbs_comment_compact |
111            decorator_expression |
112            decorator_block |
113            partial_expression |
114            partial_block )* }
115
116parameter = _{ param ~ EOI }
117handlebars = _{ template ~ EOI }
118
119// json path visitor
120// Disallowed chars: Whitespace ! " # % & ' ( ) * + , . / ; < = > @ [ \ ] ^ ` { | } ~
121
122path_id = @{ symbol_char+ }
123
124path_raw_id = { (!"]" ~ ANY)* }
125path_sep = _{ "/" | "." }
126path_up = { ".." }
127path_key = _{ "[" ~  path_raw_id ~ "]" }
128path_root = { "@root" }
129path_current = _{ "this" ~ path_sep | "./" }
130path_item = _{ path_id|path_key }
131path_local = { "@" }
132path_inline = ${ path_current? ~ (path_root ~ path_sep)? ~ path_local? ~ (path_up ~ path_sep)*  ~ path_item ~ (path_sep ~  path_item)* }
133path = _{ path_inline ~ EOI }
134