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 '{/}'