BNF for FMParser

NON-TERMINALS


// Now the actual parsing code, starting
// with the productions for FreeMarker's
// expression syntax.

/**
 * An Expression production with error recovery
 * The start Token is the token just before the expression
 * we are trying to parse, the recoverToType is 
 * the kind of token to scan ahead for to recover from 
 * errors, and the boolean consumeToken
 * says whether to consume the recoverToken or not.
 */

Expression ::= OrExpression

Exp ::= OrExpression

KeepGoingUntil ::= java code


/**
 * Lowest level expression, a literal, a variable,
 * or a possibly more complex expression bounded
 * by parentheses.
 */
PrimaryExpression ::= ( NumberLiteral | HashLiteral | StringLiteral | BooleanLiteral | NullLiteral | ListLiteral | Identifier | Parenthesis | BuiltinVariable ) ( AddSubExpression )*

Parenthesis ::= <OPEN_PAREN> Expression <CLOSE_PAREN>

/**
 * A primary expression preceded by zero or
 * more unary operators. 
 */
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 ( ( <GREATER_THAN_EQUALS> | <ESCAPED_GTE> | <GREATER_THAN> | <ESCAPED_GT> | <LESS_THAN_EQUALS> | <LESS_THAN> ) RangeExpression )?

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




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

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

ListLiteral ::= <OPEN_BRACKET> ( Exp ( ( <COMMA> )? Exp )* )? <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 | Exists | DefaultTo )

DefaultTo ::= ( <EXCLAM> ( PrimaryExpression )? )

Exists ::= <EXISTS>



BuiltIn ::= <BUILT_IN> <ID>

/**
 * production for when a key is specified by <DOT> + keyname
 */
DotVariable ::= <DOT> <ID>

/**
 * production for when the key is specified
 * in brackets.
 */
DynamicKey ::= <OPEN_BRACKET> Exp <CLOSE_BRACKET>

/**
 * production for an arglist part of a method invocation.
 */
MethodArgs ::= <OPEN_PAREN> ( ArgsList )? <CLOSE_PAREN>

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

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

NullLiteral ::= <NULL>


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

/**
 * A production representing the ${...}
 * that outputs a variable.
 */
StringOutput ::= <OUTPUT_ESCAPE> Expression <CLOSE_BRACE>

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

If ::= <IF> Exp <DIRECTIVE_END> OptionalBlock ( <ELSE_IF> Exp LooseDirectiveEnd OptionalBlock )* ( <ELSE> OptionalBlock )? CloseDirectiveBlock

Attempt ::= <ATTEMPT> OptionalBlock Recover CloseDirectiveBlock

Recover ::= <RECOVER> OptionalBlock

List ::= <LIST> Expression <AS> <ID> <DIRECTIVE_END> OptionalBlock CloseDirectiveBlock

ForEach ::= <FOREACH> <ID> <IN> Exp <DIRECTIVE_END> OptionalBlock CloseDirectiveBlock

Visit ::= <VISIT> Exp ( <USING> Exp )? LooseDirectiveEnd

Recurse ::= ( <SIMPLE_RECURSE> | ( <RECURSE> ( Exp )? ( <USING> Exp )? 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> Exp LooseDirectiveEnd )

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

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

Flush ::= <FLUSH>

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

Assign ::= ( <ASSIGN> | <GLOBALASSIGN> | <LOCALASSIGN> | <SET> ) IdentifierOrStringLiteral ( ( <EQUALS> Exp ( ( <COMMA> )? IdentifierOrStringLiteral <EQUALS> Exp )* ( <IN> Exp )? LooseDirectiveEnd )| ( ( <IN> Exp )? <DIRECTIVE_END> OptionalBlock CloseDirectiveBlock ) )


Include ::= ( <_INCLUDE> | <EMBED> ) Exp ( <SEMICOLON> )? ( <ID> <EQUALS> Exp )* LooseDirectiveEnd

Import ::= <IMPORT> Exp <AS> <ID> LooseDirectiveEnd

ParameterList ::= ( <ID> ( <ELLIPSIS> )? ( <EQUALS> Exp )? ( <COMMA> )? )*

Param ::= <PARAM> IdentifierOrStringLiteral ( <OPEN_PAREN> )? ParameterList ( <CLOSE_PAREN> )? ( <EMPTY_DIRECTIVE_END> | ( <DIRECTIVE_END> OptionalBlock CloseDirectiveBlock ) )

Macro ::= ( <MACRO> | <FUNCTION> ) IdentifierOrStringLiteral ( <OPEN_PAREN> )? ParameterList ( <CLOSE_PAREN> )? <DIRECTIVE_END> OptionalBlock CloseDirectiveBlock


Compress ::= <COMPRESS> OptionalBlock CloseDirectiveBlock


PositionalArgsList ::= Exp ( ( <COMMA> )? Exp )*

NamedArgsList ::= <ID> <EQUALS> Exp ( ( <COMMA> )? <ID> <EQUALS> Exp )*

ArgsList ::= ( NamedArgsList | PositionalArgsList )



UnifiedMacroTransform ::= <UNIFIED_CALL> Exp ( <TERMINATING_WHITESPACE> )? ( ArgsList )? ( <SEMICOLON> ( <TERMINATING_WHITESPACE> )? ParameterList )? ( <EMPTY_DIRECTIVE_END> | ( <DIRECTIVE_END> OptionalBlock <UNIFIED_CALL_END> ) )

Call ::= <CALL> Identifier ( <OPEN_PAREN> )? ArgsList ( <CLOSE_PAREN> )? LooseDirectiveEnd

NamedArgs ::= ( <ID> <EQUALS> Exp )+

TerseComment ::= <TERSE_COMMENT> ( <PRINTABLE_CHARS> )* <TERSE_COMMENT_END>

Comment ::= <COMMENT> ( <PRINTABLE_CHARS> )* <COMMENT_END>

NoParse ::= <NOPARSE> ( <PRINTABLE_CHARS> )* <NOPARSE_END>


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

Switch ::= <SWITCH> Exp <DIRECTIVE_END> ( <WHITESPACE> | Comment )* ( Case )* CloseDirectiveBlock

Case ::= ( <CASE> Exp <DIRECTIVE_END> | <DEFAUL> ) OptionalBlock

TrimBlock ::= ( <BLOCKTRIM> | <BLOCKTRIML> | <BLOCKTRIMR> | <BLOCKNOTRIM> ) OptionalBlock CloseDirectiveBlock
	

Escape ::= <ESCAPE> <ID> <AS> Exp <DIRECTIVE_END> OptionalBlock CloseDirectiveBlock

NoEscape ::= <NOESCAPE> OptionalBlock CloseDirectiveBlock


CloseDirectiveBlock ::= <CLOSE_DIRECTIVE_BLOCK>


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

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

Setting ::= <SETTING> <ID> <EQUALS> Exp LooseDirectiveEnd

Var ::= <VAR> ( IdentifierOrStringLiteral ( <EQUALS> Exp )? ) ( ( ( <COMMA> )? IdentifierOrStringLiteral ( <EQUALS> Exp )? ) )* LooseDirectiveEnd

/**
 * A production for FreeMarker directives.
 */
FreemarkerDirective ::= ( If | List | ForEach | Assign | Include | Import | Macro | Compress | UnifiedMacroTransform | Call | TerseComment | Comment | NoParse | Transform | Switch | Setting | Var | Break | Return | Stop | Flush | Trim | Nested | Escape | NoEscape | TrimBlock | Visit | Recurse | FallBack | Attempt )

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

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

/**
 * 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.
 */

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> Exp )* LooseDirectiveEnd ) )

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


/**
 * Root production to be used when parsing
 * an entire file.
 */
Root ::= ( HeaderElement )? OptionalBlock <EOF>

TOKENS

<DEFAULT> TOKEN : { 
<CLOSE_DIRECTIVE_BLOCK: "[/#" (~["]"])* "]" | "</#" (~[">"])* ">">
|<ATTEMPT: <START_TAG> "attempt" <CLOSE_TAG1>>
|<RECOVER: <START_TAG> "recover" <CLOSE_TAG1>>
|<BLOCKTRIM: <START_TAG> "t_lines" <CLOSE_TAG1>>
|<BLOCKTRIML: <START_TAG> "lt_lines" <CLOSE_TAG2>>
|<BLOCKTRIMR: <START_TAG> "rt_lines" <CLOSE_TAG1>>
|<BLOCKNOTRIM: <START_TAG> "nt_lines" <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>>
|<SET: <START_TAG> "set" <BLANK>>
|<_INCLUDE: <START_TAG> "include" <BLANK>>
|<IMPORT: <START_TAG> "import" <BLANK>>
|<FUNCTION: <START_TAG> "function" <BLANK>>
|<MACRO: <START_TAG> "macro" <BLANK>>
|<PARAM: <START_TAG> "param" <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>>
|<EMBED: <START_TAG> "embed" <BLANK>>
|<VAR: <START_TAG> "var" <BLANK>>
|<COMPRESS: <START_TAG> "compress" <CLOSE_TAG1>>
|<COMMENT: <START_TAG> "comment" <CLOSE_TAG1>>
|<TERSE_COMMENT: ("<" | "[") "#--">
|<NOPARSE: <START_TAG> "noparse" <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>>
|<NOESCAPE: <START_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"," ","\r","\n"])+>
|<PRINTABLE_CHARS: "$" | "#" | "<" | "[" | "{" | (~["$","<","#","[","{","\n","\r","\t"," "])+>
|<OUTPUT_ESCAPE: "${">
|<NUMERICAL_ESCAPE: "#{">
}

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

<EXPRESSION_COMMENT> SKIP : { 
<~[]>
|"-->"
|"--]"
}

<EXPRESSION, NO_SPACE_EXPRESSION> TOKEN : { 
<STRING_LITERAL: "\"" (~["\\","\""] | "\\" ~[])* "\"" | "\'" (~["\\","\'"] | "\\" ~[])* "\'">
|<RAW_STRING: "r" ("\"" (~["\""])* "\"" | "\'" (~["\'"])* "\'")>
|<FALSE: "false">
|<TRUE: "true">
|<NULL: "null">
|<INTEGER: (["0"-"9"])+>
|<DECIMAL: <INTEGER> "." <INTEGER>>
|<DOT: ".">
|<DOT_DOT: "..">
|<BUILT_IN: "?">
|<EXISTS: "??">
|<EQUALS: "=">
|<DOUBLE_EQUALS: "==">
|<NOT_EQUALS: "!=">
|<GREATER_THAN: ">">
|<GREATER_THAN_EQUALS: ">=">
|<EMPTY_DIRECTIVE_END: "/>" | "/]">
|<LESS_THAN: "lt" | "<" | "&lt;">
|<LESS_THAN_EQUALS: "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> (<LETTER> | <DIGIT>)*>
|}

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

<EXPRESSION, NO_SPACE_EXPRESSION> TOKEN : { 
<UNEXPECTED: ~[]>
}

<NO_PARSE> MORE : { 
<~[]>
}

<NO_PARSE> TOKEN : { 
<TERSE_COMMENT_END: "-->" | "--]">
|<COMMENT_END: "</#comment" (<BLANK>)* ">" | "[/#comment" (<BLANK>)* "]">
|<NOPARSE_END: "</#noparse" (<BLANK>)* ">" | "[/#noparse" (<BLANK>)* "]">
}

<PHONY> TOKEN : { 
<DIRECTIVE_END: ">" | "]">
}