Vascula v0.1.5

Grammar

This is the complete grammar, in EBNF. { } mean "zero or more", [ ] "optional", | "or". Quoted strings are literal. Whitespace between tokens inside {{ }} and {% %} is ignored.

#Template

template     = { text | output | tag | comment } ;
text         = any characters not starting "{{", "{%" or "{#" ;
output       = "{{" [ "-" ] expression [ "-" ] "}}" ;
comment      = "{#" [ "-" ] any characters [ "-" ] "#}"
             | "{%" "comment" "%}" any characters "{%" "endcomment" "%}" ;
tag          = "{%" [ "-" ] tag-body [ "-" ] "%}" ;

A - next to a delimiter removes all whitespace on that side of the markup.

#Tags

if           = "if" expression "%}" template
               { "{%" "elsif" expression "%}" template }
               [ "{%" "else" "%}" template ]
               "{%" "endif" ;
unless       = "unless" expression "%}" template
               [ "{%" "else" "%}" template ]
               "{%" "endunless" ;
case         = "case" expression "%}" { text }
               when { when }
               [ "{%" "else" "%}" template ]
               "{%" "endcase" ;
when         = "{%" "when" expression { ( "," | "or" ) expression } "%}" template ;
for          = "for" name "in" ( path | range ) { modifier } "%}" template
               [ "{%" "else" "%}" template ]
               "{%" "endfor" ;
modifier     = "limit" ":" expression | "offset" ":" expression | "reversed" ;
break        = "break" ;
continue     = "continue" ;
cycle        = "cycle" expression { "," expression } ;
assign       = "assign" name "=" expression ;
capture      = "capture" name "%}" template "{%" "endcapture" ;
render       = "render" string { "," name ":" expression } ;
form         = "form" string { "," attribute ":" expression } "%}" template "{%" "endform" ;
host-tag     = host-name [ expression ] ;

for, assign, capture and render names may not be settings, forloop or a name the application reserves. form kinds and host tag names are defined by the application.

#Expressions

expression   = and-expr { "or" and-expr } ;
and-expr     = comparison { "and" comparison } ;
comparison   = operand [ comparator operand ] ;
comparator   = "==" | "!=" | "<" | ">" | "<=" | ">=" | "contains" ;
operand      = primary { "|" filter } ;
filter       = name [ ":" arguments ] ;
arguments    = positional { "," positional } { "," named }
             | named { "," named } ;
positional   = primary ;
named        = name ":" primary ;
primary      = string | number | "true" | "false" | "nil" | "null"
             | "empty" | "blank" | range | path ;
range        = "(" ( number | path ) ".." ( number | path ) ")" ;
path         = name { "." name | "[" expression "]" } ;

#Tokens

name         = ( letter | "_" ) { letter | digit | "_" } ;
string       = '"' { character | "\" character } '"'
             | "'" { character | "\" character } "'" ;
number       = [ "-" ] digit { digit } [ "." digit { digit } ] ;

A backslash in a string takes the next character literally. A number without a decimal point is an integer.