templar 0.5.0

Lightweight, fast, and powerful templating engine
Documentation
wsc = _{ " " | "\t" | "\n" | "\r" }
ws = _{ wsc* }

// A whole template, whitespace on either end will be trimmed
template_root  = _{ SOI ~ template* ~ EOI }
template       = _{ content | comment_block | template_block | control_block }
template_inner = ${ template* }

// All templating tags
tag_start_expr    = _{ "{{" | (ws ~ "{{-") }
tag_end_expr      = _{ "}}" | ("-}}" ~ ws) }
tag_start_comment = _{ "{#" | (ws ~ "{#-") }
tag_end_comment   = _{ "#}" | ("-#}" ~ ws) }
tag_start_control = _{ "{%" | (ws ~ "{%-") }
tag_end_control   = _{ "%}" | ("-%}" ~ ws) }
tag_start         = _{ tag_start_expr | tag_start_comment | tag_start_control }
tag_end           = _{ tag_end_expr | tag_end_comment | tag_end_control }

// Expressions and expression containers
expression        = _{ ws ~ (inner | literal | function | value) ~ operation* ~ ws }
expression_cap    = !{ expression }
expression_vararg = _{ (expression_cap ~ ("," ~ expression_cap)*)? }
inner             = _{ "(" ~ expression_cap ~ ")" }
args              = { "(" ~ expression_vararg ~ ")" }

// Blocks e.g. {{ }} {# #} {% %}
content        = { (!tag_start ~ (ANY | wsc))+ }
template_block = { tag_start_expr ~ (!tag_end_expr ~ expression_cap) ~ tag_end_expr }
comment_block  = _{ tag_start_comment ~ (!tag_end_comment ~ ANY)* ~ tag_end_comment }
control_block  = _{ ctrl_block_if | ctrl_block_scope | ctrl_block_loop }

// Operations
operation = _{ ws ~ ( oper | filter ) ~ ws }
filter    = !{ "|" ~ ws ~ ident ~ args? }
function  = !{ ident ~ args }
oper  = _{ op ~ expression }

// Identifiers
ident      = ${ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_")*? }
value_key  = ${ "[" ~ string_lit ~ "]"}
value_id   = _{ ( "." ~ ident) | value_key }
root_ident = _{ "." }
value      = @{ (ident | root_ident) ~ (value_id)* }

// Literals
literal_cap = ${ literal }
literal     = _{ bool_lit | number_lit | string_lit | null_lit | array_lit | map_lit }
bool_lit    = _{ true_lit | false_lit }

true_lit    = @{ "true" | "yes" }
false_lit   = @{ "false" | "no" }
null_lit    = @{ "null" | "nil" }
number_lit  = @{ "-"? ~ ASCII_DIGIT+ }

string_lit   = ${ ("'" ~ str_single ~ "'") | ("\"" ~ str_double ~ "\"") | ("`" ~ str_backtick ~ "`") }
str_single   = @{ (!"'" ~ ("\\'" | ANY))* }
str_double   = @{ (!"\"" ~ ("\\\"" | ANY))* }
str_backtick = @{ (!"`" ~ ("\\`" | ANY))* }

array_lit   = @{ "[" ~ ws ~ expression_vararg ~ ws ~ "]"}

map_lit     = @{ "{" ~ ws ~ (map_kvp ~ ws ~ ("," ~ ws ~ map_kvp)*)? ~ ws ~ "}" }
map_kvp     = _{ literal_cap ~ ws ~ ":" ~ ws ~ expression_cap }

// Control blocks if/else
ctrl_block_if   = {
    tag_start_control ~ ws ~ kw_if ~ wsc+ ~ expression_cap ~ ws ~ tag_end_control ~
    template_inner ~
    (ctrl_block_end_if | ctrl_block_else)
}
ctrl_block_else = {
    tag_start_control ~ ws ~ kw_else ~ (wsc+ ~ kw_if ~ expression_cap)? ~ ws ~ tag_end_control ~
    template_inner ~
    (ctrl_block_end_if | ctrl_block_else)
}
ctrl_block_end_if   = @{ tag_start_control ~ ws ~ kw_end ~ wsc+ ~ kw_if ~ ws ~ tag_end_control }

// Control block scope
ctrl_block_scope = {
    tag_start_control ~ ws ~ kw_scpe ~ ws ~ tag_end_control ~
    template+ ~ ctrl_block_end_scpe
}
ctrl_block_end_scpe = _{ tag_start_control ~ ws ~ kw_end ~ wsc+ ~ kw_scpe ~ ws ~ tag_end_control }

// Control block loop
ctrl_block_loop = ${
    tag_start_control ~ ws ~ kw_for ~ wsc+ ~ value ~ wsc+ ~ kw_in ~ wsc+ ~ expression_cap ~ ws ~ tag_end_control ~
    template_inner ~ ctrl_block_end_loop
}
ctrl_block_end_loop = @{ tag_start_control ~ ws ~ kw_end ~ wsc+ ~ kw_for ~ ws ~ tag_end_control }

// Keywords
kw_if   = { "if" }
kw_else = _{ "else" }
kw_end  = _{ "end" }
kw_scpe = _{ "scope" }
kw_for  = _{ "for" }
kw_in   = _{ "in" }

// Operators
op  = _{
    op_add |
    op_sub |
    op_div |
    op_mlt |
    op_mod |
    op_and |
    op_or |
    op_eq |
    op_ne |
    op_gt |
    op_gte |
    op_lt |
    op_lte |
    op_cat |
    op_set
}
op_and = { "&&" }
op_or  = { "||" }
op_eq  = { "==" }
op_ne  = { "!=" }
op_gt  = { ">"  }
op_gte = { ">=" }
op_lt  = { "<"  }
op_lte = { "<=" }
op_add = { "+" }
op_sub = { "-" }
op_div = { "/" }
op_mlt = { "*" }
op_mod = { "%" }
op_cat = { "~" }
op_set = { "=" ~ !"=" }