start
  = expression

expression
  = operand binary
  / operand ternary
  / operand

operand
  = unary
  / '(' expression ')'
  / token

unary
  = [!~-] expression

binary
  = operator expression

ternary
  = '?' expression ':' expression

operator
  = '+' / '-' / '*' / '/' / '%' / '&&' / '||'

token
  = path
  / invoke
  / literal

path
  = v:variable p:( a:('.' word) { return a.join(''); }
               / b:('[' expression ']') { return b.join(''); } )* { return v + p.join('');}

variable
  = w:word { return w; }

word
  = v:[$_a-zA-Z] c:[$_a-zA-Z0-9]* { return v + c.join(''); }

invoke
  = path '(' p:items? ')'

literal
  = number
  / string
  / boolean
  / array
  / object
  / 'null'

boolean
  = 'true' / 'false'

number
  = d:[0-9]+ { return d.join(''); }

string
  = s:( '"' (c:[^"]* { return c.join(''); }) '"' ) { return s.join(''); }
  / s:( "'" (c:[^']* { return c.join(''); }) "'" ) { return s.join(''); }

array
  = '[' c:items? ']' { return '[' + (c ? c.join('') : '') + ']'; }

items
  = expression (m:( ',' i:expression { return i } )* { return (m.length ? ',' : '') + m.join(','); } )

object
  = '{' c:pairs? '}' { return '{' + (c ? c.join('') : '') + '}'; }

pairs
  = p:pair (m:( ',' i:pair { return i; } )* { return (m.length ? ',' : '') + m.join(','); })

pair
  = k:key ':' v:expression { return k + ':' + v; }

key
  = word
  / string

section
  = text
  / var
  / if
  / for

var
  = '{$' let ( m:( ',' i:let ) { return (m.length ? ',' : '') + m.join(''); } ) '/}'

let
  = word '=' expression

if
  = '{?' expression '}' section elseif* else? '{/}'

for
  = '{#' expression ':' word ( '@' word )? '}' section '{/}'