// 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> |
<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" | ["<"] | "<"> |<LESS_THAN_EQUALS: "lte" | "\\lte" | "<=" | "<="> |<ESCAPED_GT: "gt" | "\\gt" | ">"> |<ESCAPED_GTE: "gte" | "\\gte" | ">="> |<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: ["-","<","["]> } |