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