WHITESPACE = _{ " "|"\t"|"\n"|"\r" } keywords = { "as" | "else" } escape = @{ ("\\" ~ "{{" ~ "{{"?) | ("\\" ~ "\\"+ ~ &"{{") } raw_text = ${ ( escape | (!"{{" ~ ANY) )+ } raw_block_text = ${ ( escape | (!"{{{{" ~ ANY) )* } literal = { string_literal | array_literal | object_literal | number_literal | null_literal | boolean_literal } null_literal = @{ "null" ~ !symbol_char } boolean_literal = @{ ("true"|"false") ~ !symbol_char } number_literal = @{ "-"? ~ ASCII_DIGIT+ ~ "."? ~ ASCII_DIGIT* ~ ("E" ~ "-"? ~ ASCII_DIGIT+)? } json_char = { !("\"" | "\\") ~ ANY | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4}) } string_inner = @{ json_char* } string_literal = ${ "\"" ~ string_inner ~ "\"" } array_literal = { "[" ~ literal? ~ ("," ~ literal)* ~ "]" } object_literal = { "{" ~ (string_literal ~ ":" ~ literal)? ~ ("," ~ string_literal ~ ":" ~ literal)* ~ "}" } symbol_char = _{ASCII_ALPHANUMERIC|"-"|"_"|"$"|'\u{80}'..'\u{7ff}'|'\u{800}'..'\u{ffff}'|'\u{10000}'..'\u{10ffff}'} partial_symbol_char = _{ASCII_ALPHANUMERIC|"-"|"_"|'\u{80}'..'\u{7ff}'|'\u{800}'..'\u{ffff}'|'\u{10000}'..'\u{10ffff}'|"/"|"."} path_char = _{ "/" } identifier = @{ symbol_char+ } partial_identifier = @{ partial_symbol_char+ | ("[" ~ ANY+ ~ "]") | ("'" ~ (!"'" ~ ("\\'" | ANY))+ ~ "'") } reference = ${ path_inline } name = _{ subexpression | reference } param = { !(keywords ~ !symbol_char) ~ (literal | reference | subexpression) } hash = { identifier ~ "=" ~ param } block_param = { "as" ~ "|" ~ identifier ~ identifier? ~ "|"} exp_line = _{ identifier ~ (hash|param)* ~ block_param?} partial_exp_line = _{ ((partial_identifier|name) ~ (hash|param)*) } subexpression = { "(" ~ ((identifier ~ (hash|param)+) | reference) ~ ")" } pre_whitespace_omitter = { "~" } pro_whitespace_omitter = { "~" } expression = { !invert_tag ~ "{{" ~ pre_whitespace_omitter? ~ ((identifier ~ (hash|param)+) | name ) ~ pro_whitespace_omitter? ~ "}}" } html_expression_triple_bracket = _{ "{{{" ~ pre_whitespace_omitter? ~ ((identifier ~ (hash|param)+) | name ) ~ pro_whitespace_omitter? ~ "}}}" } amp_expression = _{ "{{" ~ pre_whitespace_omitter? ~ "&" ~ name ~ pro_whitespace_omitter? ~ "}}" } html_expression = { html_expression_triple_bracket | amp_expression } decorator_expression = { "{{" ~ pre_whitespace_omitter? ~ "*" ~ exp_line ~ pro_whitespace_omitter? ~ "}}" } partial_expression = { "{{" ~ pre_whitespace_omitter? ~ ">" ~ partial_exp_line ~ pro_whitespace_omitter? ~ "}}" } invert_tag_item = { "else"|"^" } invert_tag = { !escape ~ "{{" ~ pre_whitespace_omitter? ~ invert_tag_item ~ pro_whitespace_omitter? ~ "}}" } helper_block_start = { "{{" ~ pre_whitespace_omitter? ~ "#" ~ exp_line ~ pro_whitespace_omitter? ~ "}}" } helper_block_end = { "{{" ~ pre_whitespace_omitter? ~ "/" ~ identifier ~ pro_whitespace_omitter? ~ "}}" } helper_block = _{ helper_block_start ~ template ~ (invert_tag ~ template)? ~ helper_block_end } decorator_block_start = { "{{" ~ pre_whitespace_omitter? ~ "#" ~ "*" ~ exp_line ~ pro_whitespace_omitter? ~ "}}" } decorator_block_end = { "{{" ~ pre_whitespace_omitter? ~ "/" ~ identifier ~ pro_whitespace_omitter? ~ "}}" } decorator_block = _{ decorator_block_start ~ template ~ decorator_block_end } partial_block_start = { "{{" ~ pre_whitespace_omitter? ~ "#" ~ ">" ~ partial_exp_line ~ pro_whitespace_omitter? ~ "}}" } partial_block_end = { "{{" ~ pre_whitespace_omitter? ~ "/" ~ partial_identifier ~ pro_whitespace_omitter? ~ "}}" } partial_block = _{ partial_block_start ~ template ~ partial_block_end } raw_block_start = { "{{{{" ~ pre_whitespace_omitter? ~ exp_line ~ pro_whitespace_omitter? ~ "}}}}" } raw_block_end = { "{{{{" ~ pre_whitespace_omitter? ~ "/" ~ identifier ~ pro_whitespace_omitter? ~ "}}}}" } raw_block = _{ raw_block_start ~ raw_block_text ~ raw_block_end } hbs_comment = { "{{!" ~ "--" ~ (!"--}}" ~ ANY)* ~ "--" ~ "}}" } hbs_comment_compact = { "{{!" ~ (!"}}" ~ ANY)* ~ "}}" } template = { ( raw_text | expression | html_expression | helper_block | raw_block | hbs_comment | hbs_comment_compact | decorator_expression | decorator_block | partial_expression | partial_block )* } parameter = _{ param ~ EOI } handlebars = _{ template ~ EOI } // json path visitor // Disallowed chars: Whitespace ! " # % & ' ( ) * + , . / ; < = > @ [ \ ] ^ ` { | } ~ path_id = @{ symbol_char+ } path_raw_id = { (!"]" ~ ANY)* } path_sep = _{ "/" | "." } path_up = { ".." } path_key = _{ "[" ~ path_raw_id ~ "]" } path_root = { "@root" } path_current = _{ "this" ~ path_sep | "./" } path_item = _{ path_id|path_key } path_local = { "@" } path_inline = ${ path_current? ~ (path_root ~ path_sep)? ~ path_local? ~ (path_up ~ path_sep)* ~ path_item ~ (path_sep ~ path_item)* } path = _{ path_inline ~ EOI }