📄 parser.jjt
字号:
* We add whitespace in here as a token so the VMs can * easily reconstruct a macro body from the token stream * See Directive() */int DirectiveArg() #void : {}{ Reference() { return ParserTreeConstants.JJTREFERENCE; }| Word() { return ParserTreeConstants.JJTWORD; }| StringLiteral() { return ParserTreeConstants.JJTSTRINGLITERAL; }| NumberLiteral() { return ParserTreeConstants.JJTNUMBERLITERAL; }| LOOKAHEAD( <LBRACKET> [<WHITESPACE>] ( Reference() | NumberLiteral()) [<WHITESPACE>] <DOUBLEDOT> ) IntegerRange() { return ParserTreeConstants.JJTINTEGERRANGE; }| ObjectArray() { return ParserTreeConstants.JJTOBJECTARRAY; }| True() { return ParserTreeConstants.JJTTRUE; }| False() { return ParserTreeConstants.JJTFALSE; }}/** * Supports the Pluggable Directives * #foo( arg+ ) */SimpleNode Directive() :{ Token t = null; int argType; int argPos = 0; Directive d; int directiveType; boolean isVM = false; boolean doItNow = false;}{ /* * note that if we were escaped, that is now handled by * EscapedDirective() */ t = <WORD> { String directiveName = t.image.substring(1); d = (Directive) directives.get(directiveName); /* * Velocimacro support : if the directive is macro directive * then set the flag so after the block parsing, we add the VM * right then. (So available if used w/in the current template ) */ if (directiveName.equals("macro")) { doItNow = true; } /* * set the directive name from here. No reason for the thing to know * about parser tokens */ jjtThis.setDirectiveName(directiveName); if ( d == null) { /* * if null, then not a real directive, but maybe a Velocimacro */ isVM = rsvc.isVelocimacro(directiveName, currentTemplateName); if (!isVM) { token_source.stateStackPop(); token_source.inDirective = false; return jjtThis; } /* * Currently, all VMs are LINE directives */ directiveType = Directive.LINE; } else { directiveType = d.getType(); } /* * now, switch us out of PRE_DIRECTIVE */ token_source.SwitchTo(DIRECTIVE); argPos = 0; } /* * if this is indeed a token, match the #foo ( arg ) pattern */ [<WHITESPACE>] <LPAREN> ( LOOKAHEAD(2) [<WHITESPACE>] argType = DirectiveArg() { if (argType == ParserTreeConstants.JJTWORD) { if (doItNow && argPos == 0) { /* if a VM and it's the 0th arg... ok */ ; } else if( t.image.equals("#foreach") && argPos == 1) { /* if a foreach and it's the 2nd arg ok */ ; } else { throw new MacroParseException("Invalid arg #" + argPos + " in " + (isVM ? "VM " : "directive " ) + t.image + " at line " + t.beginLine + ", column " + t.beginColumn + " in template " + currentTemplateName); } } else { if (doItNow && argPos == 0) { /* if a VM and it's the 0th arg, not ok */ throw new MacroParseException("Invalid first arg " + " in #macro() directive - must be a" + " word token (no \' or \" surrounding)" + " at line " + t.beginLine + ", column " + t.beginColumn + " in template " + currentTemplateName); } } argPos++; } )* [<WHITESPACE>] <RPAREN> { if (directiveType == Directive.LINE) { return jjtThis; } } /* * and the following block if the PD needs it */ ( Statement() )+ #Block <END> { /* * VM : if we are processing a #macro directive, we need to * process the block. In truth, I can just register the name * and do the work later when init-ing. That would work * as long as things were always defined before use. This way * we don't have to worry about forward references and such... */ if (doItNow) { Macro.processAndRegister(rsvc, jjtThis, currentTemplateName); } /* * VM : end */ return jjtThis; }}void ObjectArray() : {}{ <LBRACKET> [ Parameter() ( <COMMA> Parameter() )* ] <RBRACKET>}/** * supports the [n..m] vector generator for use in * the #foreach() to generate measured ranges w/o * needing explicit support from the app/servlet */void IntegerRange() : {}{ <LBRACKET> [<WHITESPACE>] ( Reference() | NumberLiteral()) [<WHITESPACE>] <DOUBLEDOT> [<WHITESPACE>] (Reference() | NumberLiteral()) [<WHITESPACE>] <RBRACKET>}/** * This method has yet to be fully implemented * but will allow arbitrarily nested method * calls */void Parameter() #void: {}{ [<WHITESPACE>] ( StringLiteral() | LOOKAHEAD( <LBRACKET> [<WHITESPACE>] ( Reference() | NumberLiteral()) [<WHITESPACE>] <DOUBLEDOT> ) IntegerRange() | ObjectArray() | True() | False() | Reference() | NumberLiteral() ) [ <WHITESPACE>]}/** * This method has yet to be fully implemented * but will allow arbitrarily nested method * calls */void Method() : {}{ Identifier() <LPAREN> [ Parameter() ( <COMMA> Parameter() )* ] <REFMOD2_RPAREN>}void Reference() : {}{ /* * A reference is either ${<FOO>} or $<FOO> */ ( <IDENTIFIER> (LOOKAHEAD(2) <DOT> (LOOKAHEAD(3) Method() | Identifier() ))* ) | ( <LCURLY> <IDENTIFIER> (LOOKAHEAD(2) <DOT> (LOOKAHEAD(3) Method() | Identifier() ))* <RCURLY> )}void True() : {}{ <TRUE>}void False() : {}{ <FALSE>}/** * This method is responsible for allowing * all non-grammar text to pass through * unscathed. */void Text() : {}{ <TEXT>| <DOT>| <RPAREN>| <LPAREN>| <NUMBER_LITERAL>| <STRING_LITERAL>| <ESCAPE>| <LCURLY>| <RCURLY>}/* ----------------------------------------------------------------------- * * Defined Directive Syntax * * ----------------------------------------------------------------------*/void IfStatement() : {}{ <IF_DIRECTIVE> [<WHITESPACE>] <LPAREN> Expression() <RPAREN> ( Statement() )+ #Block [ LOOKAHEAD(1) ( ElseIfStatement() )+ ] [ LOOKAHEAD(1) ElseStatement() ] <END>}void ElseStatement() : {}{ <ELSE_DIRECTIVE> ( Statement() )+ #Block}void ElseIfStatement() : {}{ <ELSEIF_DIRECTIVE> [<WHITESPACE>] <LPAREN> Expression() <RPAREN> ( Statement() )+ #Block}/** * Currently support both types of set : * #set( expr ) * #set expr */void SetDirective() : {}{ <SET_DIRECTIVE>([<WHITESPACE>] Reference() [<WHITESPACE>] <EQUALS> Expression() <RPAREN> { /* * ensure that inSet is false. Leads to some amusing bugs... */ token_source.inSet = false; } [<NEWLINE>] )}/** * This method corresponds to the #stop * directive which just simulates and EOF * so that parsing stops. The #stop directive * is useful for end-user debugging * purposes. */void StopStatement() #void: {}{ <STOP_DIRECTIVE>}/* ----------------------------------------------------------------------- * * Expression Syntax * * ----------------------------------------------------------------------*/void Expression() : {}{// LOOKAHEAD( PrimaryExpression() <EQUALS> ) Assignment()//|ConditionalOrExpression()}void Assignment() #Assignment(2) : {}{ PrimaryExpression() <EQUALS> Expression()}void ConditionalOrExpression() #void : {}{ ConditionalAndExpression() ( <LOGICAL_OR> ConditionalAndExpression() #OrNode(2) )*}void ConditionalAndExpression() #void : {}{ EqualityExpression() ( <LOGICAL_AND> EqualityExpression() #AndNode(2) )*}void EqualityExpression() #void : {}{ RelationalExpression() ( <LOGICAL_EQUALS> RelationalExpression() #EQNode(2) | <LOGICAL_NOT_EQUALS> RelationalExpression() #NENode(2) )*}void RelationalExpression() #void : {}{ AdditiveExpression() ( <LOGICAL_LT> AdditiveExpression() #LTNode(2) | <LOGICAL_GT> AdditiveExpression() #GTNode(2) | <LOGICAL_LE> AdditiveExpression() #LENode(2) | <LOGICAL_GE> AdditiveExpression() #GENode(2) )*}void AdditiveExpression() #void : {}{ MultiplicativeExpression() ( <PLUS> MultiplicativeExpression() #AddNode(2) | <MINUS> MultiplicativeExpression() #SubtractNode(2) )*}void MultiplicativeExpression() #void : {}{ UnaryExpression() ( <MULTIPLY> UnaryExpression() #MulNode(2) | <DIVIDE> UnaryExpression() #DivNode(2) | <MODULUS> UnaryExpression() #ModNode(2) )*}void UnaryExpression() #void : {}{ LOOKAHEAD(2) [<WHITESPACE>] <LOGICAL_NOT> UnaryExpression() #NotNode(1)| PrimaryExpression()}void PrimaryExpression() #void : {}{ [<WHITESPACE>] ( StringLiteral() | NumberLiteral() | Reference() | LOOKAHEAD( <LBRACKET> [<WHITESPACE>] ( Reference() | NumberLiteral()) [<WHITESPACE>] <DOUBLEDOT> ) IntegerRange() | ObjectArray() | True() | False() | <LPAREN> Expression() <RPAREN> ) [<WHITESPACE>]}/* ====================================================================== Notes ----- template == the input stream for this parser, contains 'VTL' mixed in with 'schmoo' VTL == Velocity Template Language : the references, directives, etc shmoo == the non-VTL component of a template reference == VTL entity that represents data within the context. ex. $foo directive == VTL entity that denotes 'action' (#set, #foreach, #if ) defined directive (DD) == VTL directive entity that is expressed explicitly w/in this grammar pluggable directive (PD) == VTL directive entity that is defined outside of the grammar. PD's allow VTL to be easily expandable w/o parser modification. The problem with parsing VTL is that an input stream consists generally of little bits of VTL mixed in with 'other stuff, referred to as 'schmoo'. Unlike other languages, like C or Java, where the parser can punt whenever it encounters input that doesn't conform to the grammar, the VTL parser can't do that. It must simply output the schmoo and keep going. There are a few things that we do here : - define a set of parser states (DEFAULT, DIRECTIVE, REFERENCE, etc) - define for each parser state a set of tokens for each state - define the VTL grammar, expressed (mostly) in the productions such as Text(), SetStatement(), etc. It is clear that this expression of the VTL grammar (the contents of this .jjt file) is maturing and evolving as we learn more about how to parse VTL ( and as I learn about parsing...), so in the event this documentation is in disagreement w/ the source, the source takes precedence. :) Parser States ------------- DEFAULT : This is the base or starting state, and strangely enough, the default state. PRE_DIRECTIVE : State immediately following '#' before we figure out which defined or pluggable directive (or neither) we are working with. DIRECTIVE : This state is triggered by the a match of a DD or a PD. REFERENCE : Triggered by '$'. Analagous to PRE_DIRECTIVE. REFMODIFIER : Triggered by .<alpha> when in REFERENCE. REFMOD2 : Triggered by ( when in REFMODIFIER (cont) Escape Sequences ---------------- The escape processing in VTL is very simple. The '\' character acts only as an escape when : 1) On or more touch a VTL element. A VTL element is either : 1) It preceeds a reference that is in the context. 2) It preceeds a defined directive (#set, #if, #end, etc) or a valid pluggable directive, such as #foreach In all other cases the '\' is just another piece of text. The purpose of this is to allow the non-VTL parts of a template (the 'schmoo') to not have to be altered for processing by Velocity. So if in the context $foo and $bar were defined and $woogie was not \$foo \$bar \$woogie would output $foo $bar \$woogie Further, you can stack them and they affect left to right, just like convention escape characters in other languages. \$foo = $foo \\$foo = \<foo> \\\$foo = \$foo What You Expect --------------- The recent versions of the parser are trying to support precise output to support general template use. The directives do not render trailing whitespace and newlines if followed by a newline. They will render preceeding whitespace. The only exception is #set, which also eats preceeding whitespace. So, with a template : ------ #set $foo="foo" #if($foo) \$foo = $foo #end ------ it will render precisely : ------ $foo = foo ------*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -