# BNF for FMParser

## NON-TERMINALS

    // Now the actual parsing code, starting

    // with the productions for FreeMarker's

    // expression syntax.

    /**
     * This is the same as OrExpression, since
     * the OR is the operator with the lowest
     * precedence.
     */

Expression | ::= | OrExpression

    /**
     * Lowest level expression, a literal, a variable,
     * or a possibly more complex expression bounded
     * by parentheses.
     */

PrimaryExpression | ::= | ( NumberLiteral | HashLiteral | StringLiteral | BooleanLiteral | ListLiteral | Identifier | Parenthesis | BuiltinVariable ) ( AddSubExpression )*

Parenthesis | ::= | <OPEN_PAREN> Expression <CLOSE_PAREN>

    /**
     * A primary expression preceded by zero or
     * more unary operators. (The only unary operator we
     * currently have is the NOT.)
     */

UnaryExpression | ::= | ( UnaryPlusMinusExpression | NotExpression | PrimaryExpression )

NotExpression | ::= | ( <EXCLAM> )+ PrimaryExpression

UnaryPlusMinusExpression | ::= | ( <PLUS> | <MINUS> ) PrimaryExpression

AdditiveExpression | ::= | MultiplicativeExpression ( ( ( <PLUS> | <MINUS> ) ) MultiplicativeExpression )*

    /**
     * A unary expression followed by zero or more
     * unary expressions with operators in between.
     */

MultiplicativeExpression | ::= | UnaryExpression ( ( ( <TIMES> | <DIVIDE> | <PERCENT> ) ) UnaryExpression )*

EqualityExpression | ::= | RelationalExpression ( ( <NOT_EQUALS> | <EQUALS> | <DOUBLE_EQUALS> ) RelationalExpression )?

RelationalExpression | ::= | RangeExpression ( ( <NATURAL_GTE> | <ESCAPED_GTE> | <NATURAL_GT> | <ESCAPED_GT> | <LESS_THAN_EQUALS> | <LESS_THAN> ) RangeExpression )?

RangeExpression | ::= | AdditiveExpression ( <DOT_DOT> ( AdditiveExpression )? )?

AndExpression | ::= | EqualityExpression ( <AND> EqualityExpression )*

OrExpression | ::= | AndExpression ( <OR> AndExpression )*

ListLiteral | ::= | <OPEN_BRACKET> PositionalArgs <CLOSE_BRACKET>

NumberLiteral | ::= | ( <INTEGER> | <DECIMAL> )

Identifier | ::= | <ID>

IdentifierOrStringLiteral | ::= | ( Identifier | StringLiteral )

BuiltinVariable | ::= | <DOT> <ID>

    /**
     * Production that builds up an expression
     * using the dot or dynamic key name
     * or the args list if this is a method invocation.
     */

AddSubExpression | ::= | ( DotVariable | DynamicKey | MethodArgs | BuiltIn | DefaultTo | Exists )

DefaultTo | ::= | ( <TERMINATING_EXCLAM> | ( <EXCLAM> ( Expression )? ) )

Exists | ::= | <EXISTS>

BuiltIn | ::= | <BUILT_IN> <ID>

    /**
     * production for when a key is specified by <DOT> + keyname
     */

DotVariable | ::= | <DOT> ( <ID> | <TIMES> | <DOUBLE_STAR> | ( <LESS_THAN> | <LESS_THAN_EQUALS> | <ESCAPED_GT> | <ESCAPED_GTE> | <FALSE> | <TRUE> | <IN> | <AS> | <USING> ) )

    /**
     * production for when the key is specified
     * in brackets.
     */

DynamicKey | ::= | <OPEN_BRACKET> Expression <CLOSE_BRACKET>

    /**
     * production for an arglist part of a method invocation.
     */

MethodArgs | ::= | <OPEN_PAREN> PositionalArgs <CLOSE_PAREN>

StringLiteral | ::= | ( <STRING_LITERAL> | <RAW_STRING> )

BooleanLiteral | ::= | ( <FALSE> | <TRUE> )

HashLiteral | ::= | <OPEN_BRACE> ( Expression ( <COMMA> | <COLON> ) Expression ( <COMMA> Expression ( <COMMA> | <COLON> ) Expression )* )? <CLOSE_BRACE>

    /**
     * A production representing the ${...}
     * that outputs a variable.
     */

StringOutput | ::= | <OUTPUT_ESCAPE> Expression <CLOSE_BRACE>

NumericalOutput | ::= | <NUMERICAL_ESCAPE> Expression ( <SEMICOLON> <ID> )? <CLOSE_BRACE>

If | ::= | <IF> Expression <DIRECTIVE_END> OptionalBlock ( <ELSE_IF> Expression LooseDirectiveEnd OptionalBlock )* ( <ELSE> OptionalBlock )? <END_IF>

Attempt | ::= | <ATTEMPT> OptionalBlock Recover ( <END_RECOVER> | <END_ATTEMPT> )

Recover | ::= | <RECOVER> OptionalBlock

List | ::= | <LIST> Expression <AS> <ID> <DIRECTIVE_END> OptionalBlock <END_LIST>

ForEach | ::= | <FOREACH> <ID> <IN> Expression <DIRECTIVE_END> OptionalBlock <END_FOREACH>

Visit | ::= | <VISIT> Expression ( <USING> Expression )? LooseDirectiveEnd

Recurse | ::= | ( <SIMPLE_RECURSE> | ( <RECURSE> ( Expression )? ( <USING> Expression )? LooseDirectiveEnd ) )

FallBack | ::= | <FALLBACK>

    /**
     * Production used to break out of a loop or a switch block.
     */

Break | ::= | <BREAK>

    /**
     * Production used to jump out of a macro.
     * The stop instruction terminates the rendering of the template.
     */

Return | ::= | ( <SIMPLE_RETURN> | <RETURN> Expression LooseDirectiveEnd )

Stop | ::= | ( <HALT> | <STOP> Expression LooseDirectiveEnd )

Nested | ::= | ( ( <SIMPLE_NESTED> )| ( <NESTED> PositionalArgs LooseDirectiveEnd ) )

Flush | ::= | <FLUSH>

Trim | ::= | ( <TRIM> | <LTRIM> | <RTRIM> | <NOTRIM> )

Assign | ::= | ( <ASSIGN> | <GLOBALASSIGN> | <LOCALASSIGN> ) IdentifierOrStringLiteral ( ( <EQUALS> Expression ( ( <COMMA> )? IdentifierOrStringLiteral <EQUALS> Expression )* ( <IN> Expression )? LooseDirectiveEnd )| ( ( <IN> Expression )? <DIRECTIVE_END> OptionalBlock ( <END_LOCAL> | <END_ASSIGN> | <END_GLOBAL> ) ) )

Include | ::= | <INCLUDE> Expression ( <SEMICOLON> )? ( <ID> <EQUALS> Expression )* LooseDirectiveEnd

Import | ::= | <IMPORT> Expression <AS> <ID> LooseDirectiveEnd

Macro | ::= | ( <MACRO> | <FUNCTION> ) IdentifierOrStringLiteral ( <OPEN_PAREN> )? ( <ID> ( <ELLIPSIS> )? ( <EQUALS> Expression )? ( <COMMA> )? )* ( <CLOSE_PAREN> )? <DIRECTIVE_END> OptionalBlock ( <END_MACRO> | <END_FUNCTION> )

Compress | ::= | <COMPRESS> OptionalBlock <END_COMPRESS>

UnifiedMacroTransform | ::= | <UNIFIED_CALL> Expression ( <TERMINATING_WHITESPACE> )? ( NamedArgs | PositionalArgs ) ( <SEMICOLON> ( ( <TERMINATING_WHITESPACE> )? <ID> ( ( <TERMINATING_WHITESPACE> )? <COMMA> ( <TERMINATING_WHITESPACE> )? <ID> )* )? )? ( <EMPTY_DIRECTIVE_END> | ( <DIRECTIVE_END> OptionalBlock <UNIFIED_CALL_END> ) )

Call | ::= | <CALL> <ID> ( NamedArgs | ( ( <OPEN_PAREN> )? PositionalArgs ( <CLOSE_PAREN> )? ) ) LooseDirectiveEnd

NamedArgs | ::= | ( <ID> <EQUALS> Expression )+

PositionalArgs | ::= | ( Expression ( ( <COMMA> )? Expression )* )?

Comment | ::= | ( <COMMENT> | <TERSE_COMMENT> ) UnparsedContent

NoParse | ::= | <NOPARSE> UnparsedContent

Transform | ::= | <TRANSFORM> Expression ( <SEMICOLON> )? ( <ID> <EQUALS> Expression )* ( <EMPTY_DIRECTIVE_END> | ( <DIRECTIVE_END> OptionalBlock <END_TRANSFORM> ) )

Switch | ::= | <SWITCH> Expression <DIRECTIVE_END> ( Case )* ( <WHITESPACE> )? <END_SWITCH>

Case | ::= | ( <WHITESPACE> )? ( <CASE> Expression <DIRECTIVE_END> | <DEFAUL> ) OptionalBlock

Escape | ::= | <ESCAPE> <ID> <AS> Expression <DIRECTIVE_END> OptionalBlock <END_ESCAPE>

NoEscape | ::= | <NOESCAPE> OptionalBlock <END_NOESCAPE>

    /**
     * Production to terminate potentially empty elements. Either a ">" or "/>"
     */

LooseDirectiveEnd | ::= | ( <DIRECTIVE_END> | <EMPTY_DIRECTIVE_END> )

Setting | ::= | <SETTING> <ID> <EQUALS> Expression LooseDirectiveEnd

    /**
     * A production for FreeMarker directives.
     */

FreemarkerDirective | ::= | ( If | List | ForEach | Assign | Include | Import | Macro | Compress | UnifiedMacroTransform | Call | Comment | NoParse | Transform | Switch | Setting | Break | Return | Stop | Flush | Trim | Nested | Escape | NoEscape | Visit | Recurse | FallBack | Attempt )

    /**
     * Production for a block of raw text
     * i.e. text that contains no
     * FreeMarker directives.
     */

PCData | ::= | ( ( <WHITESPACE> | <PRINTABLE_CHARS> | <FALSE_ALERT> ) )+

    /**
     * Production for dealing with unparsed content,
     * i.e. what is inside a comment or noparse tag.
     * It returns the ending token. The content
     * of the tag is put in buf.
     */

UnparsedContent | ::= | ( ( <KEEP_GOING> | <MAYBE_END> | <TERSE_COMMENT_END> | <LONE_LESS_THAN_OR_DASH> ) )+

Content | ::= | ( ( PCData | StringOutput | NumericalOutput | FreemarkerDirective ) )+

    /**
     * A production freemarker text that may contain
     * ${...} and #{...} but no directives.
     */

FreeMarkerText | ::= | ( ( PCData | StringOutput | NumericalOutput ) )+

    /**
     * A production for a block of optional content.
     * Returns an empty Text block if there is no
     * content.
     */

OptionalBlock | ::= | ( Content )?

HeaderElement | ::= | ( <WHITESPACE> )? ( <TRIVIAL_FTL_HEADER> | ( <FTL_HEADER> ( <ID> <EQUALS> Expression )* ) LooseDirectiveEnd )

ParamList | ::= | ( Identifier <EQUALS> Expression ( <COMMA> )? )+

    /**
     * Root production to be used when parsing
     * an entire file.
     */

Root | ::= | ( HeaderElement )? OptionalBlock <EOF>

## TOKENS

    <DEFAULT> TOKEN : {
    <ATTEMPT: <START_TAG> "attempt" <CLOSE_TAG1>>
    |<RECOVER: <START_TAG> "recover" <CLOSE_TAG1>>
    |<IF: <START_TAG> "if" <BLANK>>
    |<ELSE_IF: <START_TAG> "elseif" <BLANK>>
    |<LIST: <START_TAG> "list" <BLANK>>
    |<FOREACH: <START_TAG> "foreach" <BLANK>>
    |<SWITCH: <START_TAG> "switch" <BLANK>>
    |<CASE: <START_TAG> "case" <BLANK>>
    |<ASSIGN: <START_TAG> "assign" <BLANK>>
    |<GLOBALASSIGN: <START_TAG> "global" <BLANK>>
    |<LOCALASSIGN: <START_TAG> "local" <BLANK>>
    |<INCLUDE: <START_TAG> "include" <BLANK>>
    |<IMPORT: <START_TAG> "import" <BLANK>>
    |<FUNCTION: <START_TAG> "function" <BLANK>>
    |<MACRO: <START_TAG> "macro" <BLANK>>
    |<TRANSFORM: <START_TAG> "transform" <BLANK>>
    |<VISIT: <START_TAG> "visit" <BLANK>>
    |<STOP: <START_TAG> "stop" <BLANK>>
    |<RETURN: <START_TAG> "return" <BLANK>>
    |<CALL: <START_TAG> "call" <BLANK>>
    |<SETTING: <START_TAG> "setting" <BLANK>>
    |<COMPRESS: <START_TAG> "compress" <CLOSE_TAG1>>
    |<COMMENT: <START_TAG> "comment" <CLOSE_TAG1>>
    |<TERSE_COMMENT: (["<","["]) "#--">
    |<NOPARSE: <START_TAG> "noparse" <CLOSE_TAG1>>
    |<END_IF: <END_TAG> "if" <CLOSE_TAG1>>
    |<END_LIST: <END_TAG> "list" <CLOSE_TAG1>>
    |<END_RECOVER: <END_TAG> "recover" <CLOSE_TAG1>>
    |<END_ATTEMPT: <END_TAG> "attempt" <CLOSE_TAG1>>
    |<END_FOREACH: <END_TAG> "foreach" <CLOSE_TAG1>>
    |<END_LOCAL: <END_TAG> "local" <CLOSE_TAG1>>
    |<END_GLOBAL: <END_TAG> "global" <CLOSE_TAG1>>
    |<END_ASSIGN: <END_TAG> "assign" <CLOSE_TAG1>>
    |<END_FUNCTION: <END_TAG> "function" <CLOSE_TAG1>>
    |<END_MACRO: <END_TAG> "macro" <CLOSE_TAG1>>
    |<END_COMPRESS: <END_TAG> "compress" <CLOSE_TAG1>>
    |<END_TRANSFORM: <END_TAG> "transform" <CLOSE_TAG1>>
    |<END_SWITCH: <END_TAG> "switch" <CLOSE_TAG1>>
    |<ELSE: <START_TAG> "else" <CLOSE_TAG2>>
    |<BREAK: <START_TAG> "break" <CLOSE_TAG2>>
    |<SIMPLE_RETURN: <START_TAG> "return" <CLOSE_TAG2>>
    |<HALT: <START_TAG> "stop" <CLOSE_TAG2>>
    |<FLUSH: <START_TAG> "flush" <CLOSE_TAG2>>
    |<TRIM: <START_TAG> "t" <CLOSE_TAG2>>
    |<LTRIM: <START_TAG> "lt" <CLOSE_TAG2>>
    |<RTRIM: <START_TAG> "rt" <CLOSE_TAG2>>
    |<NOTRIM: <START_TAG> "nt" <CLOSE_TAG2>>
    |<DEFAUL: <START_TAG> "default" <CLOSE_TAG1>>
    |<SIMPLE_NESTED: <START_TAG> "nested" <CLOSE_TAG2>>
    |<NESTED: <START_TAG> "nested" <BLANK>>
    |<SIMPLE_RECURSE: <START_TAG> "recurse" <CLOSE_TAG2>>
    |<RECURSE: <START_TAG> "recurse" <BLANK>>
    |<FALLBACK: <START_TAG> "fallback" <CLOSE_TAG2>>
    |<ESCAPE: <START_TAG> "escape" <BLANK>>
    |<END_ESCAPE: <END_TAG> "escape" <CLOSE_TAG1>>
    |<NOESCAPE: <START_TAG> "noescape" <CLOSE_TAG1>>
    |<END_NOESCAPE: <END_TAG> "noescape" <CLOSE_TAG1>>
    |<UNIFIED_CALL: "<@" | "[@">
    |<UNIFIED_CALL_END: (["<","["]) "/@" (<ID> ("." <ID>)*)? <CLOSE_TAG1>>
    |<FTL_HEADER: ("<#ftl" | "[#ftl") <BLANK>>
    |<TRIVIAL_FTL_HEADER: ("<#ftl" | "[#ftl") ("/")? ([">","]"])>
    |<UNKNOWN_DIRECTIVE: ("[#" | "[/#" | "<#" | "</#") (["A"-"Z","_","a"-"z"])+>
    }

    <DEFAULT, NODIRECTIVE> TOKEN : {
    <WHITESPACE: (["\t","\n","\r"," "])+>
    |<PRINTABLE_CHARS: (["\u0000"-"\b","\u000b"-"\f","\u000e"-"\u001f","!"-"\"","%"-";","="-"Z","\\"-"z","|"-"\uffff"])+>
    |<FALSE_ALERT: ["#","$","<","[","{"]>
    |<OUTPUT_ESCAPE: "${">
    |<NUMERICAL_ESCAPE: "#{">
    }

    <FM_EXPRESSION, IN_PAREN, NAMED_PARAMETER_EXPRESSION> SKIP : {
    <(["\t","\n","\r"," "])+>
    |<["<","["] ["!","#"] "--">
    }

    <EXPRESSION_COMMENT> SKIP : {
    <(["\u0000"-",","."-"=","?"-"\\","^"-"\uffff"])+>
    |">"
    |"]"
    |"-"
    |<"-->" | "--]">
    }

    <FM_EXPRESSION, IN_PAREN, NO_SPACE_EXPRESSION, NAMED_PARAMETER_EXPRESSION> TOKEN : {
    <STRING_LITERAL: "\"" (["\u0000"-"!","#"-"[","]"-"\uffff"] | <ESCAPED_CHAR>)* "\"" | "\'" (["\u0000"-"&","("-"[","]"-"\uffff"] | <ESCAPED_CHAR>)* "\'">
    |<RAW_STRING: "r" ("\"" (["\u0000"-"!","#"-"\uffff"])* "\"" | "\'" (["\u0000"-"&","("-"\uffff"])* "\'")>
    |<FALSE: "false">
    |<TRUE: "true">
    |<INTEGER: (["0"-"9"])+>
    |<DECIMAL: <INTEGER> "." <INTEGER>>
    |<DOT: ".">
    |<DOT_DOT: "..">
    |<BUILT_IN: "?">
    |<EXISTS: "??">
    |<EQUALS: "=">
    |<DOUBLE_EQUALS: "==">
    |<NOT_EQUALS: "!=">
    |<LESS_THAN: "lt" | "\\lt" | ["<"] | "&lt;">
    |<LESS_THAN_EQUALS: "lte" | "\\lte" | "<=" | "&lt;=">
    |<ESCAPED_GT: "gt" | "\\gt" | "&gt;">
    |<ESCAPED_GTE: "gte" | "\\gte" | "&gt;=">
    |<PLUS: "+">
    |<MINUS: "-">
    |<TIMES: "*">
    |<DOUBLE_STAR: "**">
    |<ELLIPSIS: "...">
    |<DIVIDE: "/">
    |<PERCENT: "%">
    |<AND: ["&"] | "&&">
    |<OR: ["|"] | "||">
    |<EXCLAM: "!">
    |<COMMA: ",">
    |<SEMICOLON: ";">
    |<COLON: ":">
    |<OPEN_BRACKET: "[">
    |<CLOSE_BRACKET: "]">
    |<OPEN_PAREN: "(">
    |<CLOSE_PAREN: ")">
    |<OPEN_BRACE: "{">
    |<CLOSE_BRACE: "}">
    |<IN: "in">
    |<AS: "as">
    |<USING: "using">
    |<ID: <LETTER> (["$","0"-"9","@"-"Z","_","a"-"z","\u00c0"-"\u00d6","\u00d8"-"\u00f6","\u00f8"-"\u00ff","\u0100"-"\u1fff","\u3040"-"\u318f","\u3300"-"\u337f","\u3400"-"\u3d2d","\u4e00"-"\u9fff","\uf900"-"\ufaff"])*>
    |}

    <FM_EXPRESSION, NO_SPACE_EXPRESSION, NAMED_PARAMETER_EXPRESSION> TOKEN : {
    <DIRECTIVE_END: ">">
    |<EMPTY_DIRECTIVE_END: "/>" | "/]">
    }

    <IN_PAREN> TOKEN : {
    <NATURAL_GT: ">">
    |<NATURAL_GTE: ">=">
    }

    <NO_SPACE_EXPRESSION> TOKEN : {
    <TERMINATING_WHITESPACE: (["\t","\n","\r"," "])+>
    }

    <NAMED_PARAMETER_EXPRESSION> TOKEN : {
    <TERMINATING_EXCLAM: "!" (["\t","\n","\r"," "])+>
    }

    <NO_PARSE> TOKEN : {
    <TERSE_COMMENT_END: "-->" | "--]">
    |<MAYBE_END: (["<","["]) "/" ("#")? (["A"-"Z","a"-"z"])+ (["\t","\n","\r"," "])* ([">","]"])>
    |<KEEP_GOING: (["\u0000"-",","."-";","="-"Z","\\"-"\uffff"])+>
    |<LONE_LESS_THAN_OR_DASH: ["-","<","["]>
    }