BNF для 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: ["-","<","["]>
}