| /*@bgen(jjtree) Generated By:JJTree: Do not edit this line. Parser.jj */ |
| /*@egen*//* |
| * The Apache Software License, Version 1.1 |
| * |
| * Copyright (c) 2001 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, if |
| * any, must include the following acknowlegement: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowlegement may appear in the software itself, |
| * if and wherever such third-party acknowlegements normally appear. |
| * |
| * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software |
| * Foundation" must not be used to endorse or promote products derived |
| * from this software without prior written permission. For written |
| * permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache" |
| * nor may "Apache" appear in their names without prior written |
| * permission of the Apache Group. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| */ |
| |
| /* |
| * NOTE : please see documentation at bottom of this file. (It was placed there its tiring |
| * to always have to page past it... :) |
| */ |
| |
| options |
| { |
| |
| /** |
| * The parser must be non-static in order for the |
| * above option to work, otherwise the parser value |
| * is passed in as null, which isn't all the useful ;) |
| */ |
| STATIC=false; |
| |
| DEBUG_PARSER=false; |
| DEBUG_TOKEN_MANAGER=false; |
| } |
| |
| PARSER_BEGIN(Parser) |
| |
| package org.apache.velocity.runtime.parser; |
| |
| import java.io.*; |
| import java.util.*; |
| |
| import org.apache.velocity.runtime.Runtime; |
| import org.apache.velocity.runtime.parser.node.*; |
| import org.apache.velocity.runtime.directive.Directive; |
| import org.apache.velocity.runtime.directive.Macro; |
| import org.apache.velocity.util.StringUtils; |
| |
| /** |
| * This class is responsible for parsing a Velocity |
| * template. This class was generated by JavaCC using |
| * the JJTree extension to produce an Abstract |
| * Syntax Tree (AST) of the template. |
| * |
| * Please look at the Parser.jjt file which is |
| * what controls the generation of this class. |
| * |
| * @author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a> |
| * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> |
| * @version $Id: Parser.jj,v 1.56 2001/04/14 02:51:16 geirm Exp $ |
| */ |
| public class Parser/*@bgen(jjtree)*/implements ParserTreeConstants/*@egen*/ |
| {/*@bgen(jjtree)*/ |
| protected JJTParserState jjtree = new JJTParserState(); |
| |
| /*@egen*/ |
| /** |
| * This Hashtable contains a list of all of the dynamic directives. |
| */ |
| private Hashtable directives = new Hashtable(0); |
| |
| /** |
| * Name of current template we are parsing. Passed to us in parse() |
| */ |
| String currentTemplateName = ""; |
| |
| /** |
| * encoding for the input stream. |
| */ |
| private String inputEncoding = "ISO-8859-1"; |
| |
| /** |
| * This constructor was added to allow the re-use of parsers. |
| * The normal constructor takes a single argument which |
| * an InputStream. This simply creates a re-usable parser |
| * object, we satisfy the requirement of an InputStream |
| * by using a newline character as an input stream. |
| */ |
| public Parser() |
| { |
| this(new ByteArrayInputStream("\n".getBytes())); |
| |
| /* |
| * get the encoding property. Default is ISO latin |
| */ |
| inputEncoding = Runtime.getString( Runtime.INPUT_ENCODING, "ISO-8859-1"); |
| } |
| |
| /** |
| * This was also added to allow parsers to be |
| * re-usable. Normal JavaCC use entails passing an |
| * input stream to the constructor and the parsing |
| * process is carried out once. We want to be able |
| * to re-use parsers: we do this by adding this |
| * method and re-initializing the lexer with |
| * the new stream that we want parsed. |
| */ |
| public SimpleNode parse(InputStream stream, String templateName ) |
| throws ParseException |
| { |
| SimpleNode sn = null; |
| |
| currentTemplateName = templateName; |
| |
| /* |
| * clearing the VM namespace for this template |
| * the VM factory / manager will determine if this |
| * is appropose, but I think this is the place to indicate |
| * that it should be done. |
| */ |
| |
| Runtime.dumpVMNamespace( currentTemplateName ); |
| |
| /* |
| * wrap in a reader so users can control the encoding |
| * of the template |
| */ |
| |
| BufferedReader br = null; |
| |
| try |
| { |
| br = new BufferedReader( new InputStreamReader( stream, inputEncoding ) ); |
| } |
| catch( UnsupportedEncodingException uce ) |
| { |
| String msg = "Parser Exception: Unsupported input encoding : " + inputEncoding |
| + " for template " + templateName; |
| Runtime.error( msg ); |
| throw new ParseException( msg ); |
| } |
| |
| try |
| { |
| token_source.clearStateVars(); |
| ReInit(br); |
| sn = process(); |
| } |
| catch (ParseException pe) |
| { |
| Runtime.error ("Parser Exception: " + templateName + " : " + StringUtils.stackTrace(pe)); |
| throw new ParseException (pe.currentToken, |
| pe.expectedTokenSequences, pe.tokenImage); |
| } |
| catch (Exception e) |
| { |
| Runtime.error ("Parser Error: " + templateName + " : " + StringUtils.stackTrace(e)); |
| } |
| |
| currentTemplateName = ""; |
| |
| return sn; |
| } |
| |
| /** |
| * This method sets the directives Hashtable |
| */ |
| public void setDirectives(Hashtable directives) |
| { |
| this.directives = directives; |
| } |
| |
| /** |
| * This method gets a Directive from the directives Hashtable |
| */ |
| public Directive getDirective(String directive) |
| { |
| return (Directive) directives.get(directive); |
| } |
| |
| /** |
| * This method finds out of the directive exists in the directives |
| * Hashtable. |
| */ |
| public boolean isDirective(String directive) |
| { |
| if (directives.containsKey(directive)) |
| return true; |
| else |
| return false; |
| } |
| |
| |
| /** |
| * Produces a processed output for an escaped control or |
| * pluggable directive |
| */ |
| private String escapedDirective( String strImage ) |
| { |
| int iLast = strImage.lastIndexOf("\\"); |
| |
| String strDirective = strImage.substring(iLast + 1); |
| |
| boolean bRecognizedDirective = false; |
| |
| /* |
| * is this a PD or a control directive? |
| */ |
| |
| if ( isDirective( strDirective.substring(1))) |
| { |
| bRecognizedDirective = true; |
| } |
| else if (Runtime.isVelocimacro( strDirective.substring(1), currentTemplateName)) |
| { |
| bRecognizedDirective = true; |
| } |
| else |
| { |
| /* order for speed? */ |
| |
| if ( strDirective.substring(1).equals("if") |
| || strDirective.substring(1).equals("end") |
| || strDirective.substring(1).equals("set") |
| || strDirective.substring(1).equals("else") |
| || strDirective.substring(1).equals("elseif") |
| || strDirective.substring(1).equals("stop") |
| ) |
| { |
| bRecognizedDirective = true; |
| } |
| } |
| |
| /* |
| * if so, make the proper prefix string (let the escapes do their thing..) |
| * otherwise, just return what it is.. |
| */ |
| |
| if (bRecognizedDirective) |
| return ( strImage.substring(0,iLast/2) + strDirective); |
| else |
| return ( strImage ); |
| } |
| } |
| |
| PARSER_END(Parser) |
| |
| TOKEN_MGR_DECLS: |
| { |
| private int fileDepth = 0; |
| |
| private int lparen = 0; |
| private int rparen = 0; |
| |
| Stack stateStack = new Stack(); |
| public boolean debugPrint = false; |
| |
| private boolean inReference; |
| public boolean inDirective; |
| private boolean inComment; |
| public boolean inSet; |
| |
| /** |
| * pushes the current state onto the 'state stack', |
| * and maintains the parens counts |
| * public because we need it in PD & VM handling |
| * |
| * @return boolean : success. It can fail if the state machine |
| * gets messed up (do don't mess it up :) |
| */ |
| public boolean stateStackPop() |
| { |
| Hashtable h; |
| |
| try |
| { |
| h = (Hashtable) stateStack.pop(); |
| } |
| catch( EmptyStackException e) |
| { |
| lparen=0; |
| SwitchTo(DEFAULT); |
| return false; |
| } |
| |
| if( debugPrint ) |
| System.out.println( |
| " stack pop (" + stateStack.size() + ") : lparen=" + |
| ( (Integer) h.get("lparen")).intValue() + |
| " newstate=" + ( (Integer) h.get("lexstate")).intValue() ); |
| |
| lparen = ( (Integer) h.get("lparen")).intValue(); |
| rparen = ( (Integer) h.get("rparen")).intValue(); |
| |
| SwitchTo( ( (Integer) h.get("lexstate")).intValue() ); |
| |
| return true; |
| } |
| |
| /** |
| * pops a state off the stack, and restores paren counts |
| * |
| * @return boolean : success of operation |
| */ |
| public boolean stateStackPush() |
| { |
| if( debugPrint ) |
| System.out.println(" (" + stateStack.size() + ") pushing cur state : " + |
| curLexState ); |
| |
| Hashtable h = new Hashtable(); |
| |
| h.put("lexstate", new Integer( curLexState ) ); |
| h.put("lparen", new Integer( lparen )); |
| h.put("rparen", new Integer( rparen )); |
| |
| lparen = 0; |
| |
| stateStack.push( h ); |
| |
| return true; |
| } |
| |
| /** |
| * Clears all state variables, resets to |
| * start values, clears stateStack. Call |
| * before parsing. |
| * @return void |
| */ |
| public void clearStateVars() |
| { |
| stateStack.clear(); |
| |
| lparen = 0; |
| rparen = 0; |
| inReference = false; |
| inDirective = false; |
| inComment = false; |
| inSet = false; |
| |
| return; |
| } |
| |
| |
| /** |
| * handles the dropdown logic when encountering a RPAREN |
| */ |
| private void RPARENHandler() |
| { |
| /* |
| * Ultimately, we want to drop down to the state below |
| * the one that has an open (if we hit bottom (DEFAULT), |
| * that's fine. It's just text schmoo. |
| */ |
| |
| boolean closed = false; |
| |
| if (inComment) |
| closed = true; |
| |
| while( !closed ) |
| { |
| /* |
| * look at current state. If we haven't seen a lparen |
| * in this state then we drop a state, because this |
| * lparen clearly closes our state |
| */ |
| |
| if( lparen > 0) |
| { |
| /* |
| * if rparen + 1 == lparen, then this state is closed. |
| * Otherwise, increment and keep parsing |
| */ |
| |
| if( lparen == rparen + 1) |
| { |
| stateStackPop(); |
| } |
| else |
| { |
| rparen++; |
| } |
| |
| closed = true; |
| } |
| else |
| { |
| /* |
| * now, drop a state |
| */ |
| |
| if(!stateStackPop()) |
| break; |
| } |
| } |
| } |
| } |
| |
| /* ------------------------------------------------------------------------ |
| * |
| * Tokens |
| * |
| * Note : we now have another state, REFMODIFIER. This is sort of a |
| * type of REFERENCE state, simply use to use the DIRECTIVE token |
| * set when we are processing a $foo.bar() construct |
| * |
| * ------------------------------------------------------------------------- */ |
| |
| <DIRECTIVE,REFMOD2> |
| TOKEN: |
| { |
| <LBRACKET: "["> |
| | <RBRACKET: "]"> |
| | <COMMA:","> |
| } |
| |
| <DIRECTIVE> |
| TOKEN: |
| { |
| <DOUBLEDOT : ".." > |
| } |
| |
| |
| <DIRECTIVE,REFMODIFIER> |
| TOKEN: |
| { |
| <LPAREN: "("> |
| { |
| if (!inComment) |
| lparen++; |
| |
| /* |
| * If in REFERENCE and we have seen the dot, then move |
| * to REFMOD2 -> Modifier() |
| */ |
| |
| if (curLexState == REFMODIFIER ) |
| SwitchTo( REFMOD2 ); |
| } |
| } |
| |
| /* |
| * we never will see a ')' in anything but DIRECTIVE and REFMOD2. |
| * Each have their own |
| */ |
| <DIRECTIVE> |
| TOKEN: |
| { |
| /* |
| * We will eat any whitespace upto and including a newline for directives |
| */ |
| <RPAREN: ")" ( ( " " | "\t" )* ( "\n" | "\r" | "\r\n" ))?> |
| { |
| RPARENHandler(); |
| } |
| } |
| |
| |
| <REFMOD2> |
| TOKEN: |
| { |
| /* |
| * in REFMOD2, we don't want to bind the whitespace and \n like we |
| * do when closing a directive. |
| */ |
| <REFMOD2_RPAREN: ")"> |
| { |
| /* |
| * need to simply switch back to REFERENCE, not drop down the stack |
| * because we can (infinitely) chain, ala |
| * $foo.bar().blargh().woogie().doogie() |
| */ |
| |
| SwitchTo( REFERENCE ); |
| } |
| } |
| |
| /*---------------------------------------------- |
| * |
| * escape "\\" handling for the built-in directives |
| * |
| *--------------------------------------------- */ |
| TOKEN: |
| { |
| /* |
| * We have to do this, because we want these to be a Text node, and |
| * whatever follows to be peer to this text in the tree. |
| * |
| * We need to touch the ASTs for these, because we want an even # of \'s |
| * to render properly in front of the block |
| * |
| * This is really simplistic. I actually would prefer to find them in |
| * grammatical context, but I am neither smart nor rested, a receipe |
| * for disaster, another long night with Mr. Parser, or both. |
| */ |
| |
| <ESCAPE_DIRECTIVE : (<DOUBLE_ESCAPE>)* "\\#" <WORD> > |
| } |
| |
| |
| /* |
| * needed because #set is so wacky in it's desired behavior. We want set |
| * to eat any preceeding whitespace so it is invisible in formatting. |
| * (As it should be.) If this works well, I am going to chuck the whole MORE: |
| * token abomination. |
| */ |
| TOKEN: |
| { |
| <SET_DIRECTIVE: (" "|"\t")* "#set" > |
| { |
| if (! inComment) |
| { |
| inDirective = true; |
| |
| if ( debugPrint ) |
| System.out.print("#set : going to " + DIRECTIVE ); |
| |
| stateStackPush(); |
| inSet = true; |
| SwitchTo(DIRECTIVE); |
| } |
| } |
| } |
| |
| <*> |
| MORE : |
| { |
| /* |
| * Note : DOLLARBANG is a duplicate of DOLLAR. They must be identical. |
| */ |
| |
| <DOLLAR: ("\\")* "$"> |
| { |
| if (! inComment) |
| { |
| /* |
| * if we find ourselves in REFERENCE, we need to pop down |
| * to end the previous ref |
| */ |
| |
| if (curLexState == REFERENCE) |
| { |
| inReference = false; |
| stateStackPop(); |
| } |
| |
| inReference = true; |
| |
| if ( debugPrint ) |
| System.out.print( "$ : going to " + REFERENCE ); |
| |
| stateStackPush(); |
| SwitchTo(REFERENCE); |
| } |
| } |
| |
| | <DOLLARBANG: ("\\")* "$" ("\\")* "!"> |
| { |
| if (! inComment) |
| { |
| /* |
| * if we find ourselves in REFERENCE, we need to pop down |
| * to end the previous ref |
| */ |
| |
| if (curLexState == REFERENCE) |
| { |
| inReference = false; |
| stateStackPop(); |
| } |
| |
| inReference = true; |
| |
| if ( debugPrint ) |
| System.out.print( "$! : going to " + REFERENCE ); |
| |
| stateStackPush(); |
| SwitchTo(REFERENCE); |
| } |
| } |
| |
| | "##" |
| { |
| if (!inComment) |
| { |
| inComment = true; |
| stateStackPush(); |
| SwitchTo(IN_SINGLE_LINE_COMMENT); |
| } |
| } |
| |
| | <"#**" ~["#"]> |
| { |
| input_stream.backup(1); |
| inComment = true; |
| stateStackPush(); |
| SwitchTo( IN_FORMAL_COMMENT); |
| } |
| |
| | "#*" |
| { |
| inComment=true; |
| stateStackPush(); |
| SwitchTo( IN_MULTI_LINE_COMMENT ); |
| } |
| |
| | <HASH : "#" > |
| { |
| if (! inComment) |
| { |
| /* |
| * We can have the situation where #if($foo)$foo#end. |
| * We need to transition out of REFERENCE before going to DIRECTIVE. |
| * I don't really like this, but I can't think of a legal way |
| * you are going into DIRECTIVE while in REFERENCE. -gmj |
| */ |
| |
| if (curLexState == REFERENCE) |
| { |
| inReference = false; |
| stateStackPop(); |
| } |
| |
| inDirective = true; |
| |
| if ( debugPrint ) |
| System.out.print("# : going to " + DIRECTIVE ); |
| |
| stateStackPush(); |
| SwitchTo(PRE_DIRECTIVE); |
| } |
| } |
| } |
| |
| TOKEN : |
| { |
| <DOUBLE_ESCAPE : "\\\\"> |
| | <ESCAPE: "\\" > |
| | <TEXT: (~["$", "#", "\\"])+ > |
| } |
| |
| /* ----------------------------------------------------------------------- |
| * |
| * *_COMMENT Lexical tokens |
| * |
| *-----------------------------------------------------------------------*/ |
| <IN_SINGLE_LINE_COMMENT> |
| TOKEN : |
| { |
| <SINGLE_LINE_COMMENT: "\n" | "\r" | "\r\n" > |
| { |
| inComment = false; |
| stateStackPop(); |
| } |
| |
| } |
| |
| <IN_FORMAL_COMMENT> |
| TOKEN : |
| { |
| <FORMAL_COMMENT: "*#" > |
| { |
| inComment = false; |
| stateStackPop(); |
| } |
| } |
| |
| <IN_MULTI_LINE_COMMENT> |
| TOKEN : |
| { |
| <MULTI_LINE_COMMENT: "*#" > |
| { |
| inComment = false; |
| stateStackPop(); |
| } |
| } |
| |
| <IN_SINGLE_LINE_COMMENT,IN_FORMAL_COMMENT,IN_MULTI_LINE_COMMENT> |
| MORE : |
| { |
| < ~[] > |
| } |
| |
| /* ----------------------------------------------------------------------- |
| * |
| * DIRECTIVE Lexical State (some of it, anyway) |
| * |
| * ---------------------------------------------------------------------- */ |
| |
| <DIRECTIVE,REFMOD2> |
| TOKEN: |
| { |
| <WHITESPACE : ([" ","\t"])+ > |
| } |
| |
| <DIRECTIVE,REFMOD2> |
| TOKEN : |
| { |
| // <STRING_LITERAL: ( "\"" ( ~["\"","\n","\r"] )* "\"" ) | ( "'" ( ~["'","\n","\r"] )* "'" ) > |
| < STRING_LITERAL: |
| ("\"" |
| ( (~["\"","\\","\n","\r"]) |
| | ("\\" |
| ( ["n","t","b","r","f","\\","'","\""] |
| | ["0"-"7"] ( ["0"-"7"] )? |
| | ["0"-"3"] ["0"-"7"] ["0"-"7"] |
| ) |
| ) |
| | ( "\\" (" ")* "\n") |
| )* |
| "\"" |
| ) |
| | |
| ("\'" |
| ( (~["\'","\n","\r"]) |
| | ( "\\" (" ")* "\n") |
| )* |
| "\'" |
| ) |
| > |
| |
| { |
| /* |
| * - if we are in DIRECTIVE and haven't seen ( yet, then also drop out. |
| * don't forget to account for the beloved yet wierd #set |
| * - finally, if we are in REFMOD2 (remember : $foo.bar( ) then " is ok! |
| */ |
| |
| if( curLexState == DIRECTIVE && !inSet && lparen == 0) |
| stateStackPop(); |
| } |
| } |
| |
| <REFERENCE,DIRECTIVE,REFMODIFIER,REFMOD2> |
| TOKEN: |
| { |
| <TRUE: "true"> |
| | <FALSE: "false"> |
| } |
| |
| <DIRECTIVE> |
| TOKEN : |
| { |
| <NEWLINE: "\n" | "\r" | "\r\n" > |
| { |
| if ( debugPrint ) |
| System.out.println(" NEWLINE :"); |
| |
| stateStackPop(); |
| |
| if (inSet) |
| inSet = false; |
| |
| if (inDirective) |
| inDirective = false; |
| } |
| } |
| |
| |
| <DIRECTIVE> |
| TOKEN : |
| { |
| <MINUS: "-"> |
| | <PLUS: "+"> |
| | <MULTIPLY: "*"> |
| | <DIVIDE: "/"> |
| | <MODULUS: "%"> |
| | <LOGICAL_AND: "&&"> |
| | <LOGICAL_OR: "||"> |
| | <LOGICAL_LT: "<"> |
| | <LOGICAL_LE: "<="> |
| | <LOGICAL_GT: ">"> |
| | <LOGICAL_GE: ">="> |
| | <LOGICAL_EQUALS: "=="> |
| | <LOGICAL_NOT_EQUALS: "!="> |
| | <LOGICAL_NOT: "!"> |
| | <EQUALS: "=" > |
| } |
| |
| <PRE_DIRECTIVE> |
| TOKEN : |
| { |
| <END: "end" ( ( " " | "\t" )* ( "\n" | "\r" | "\r\n" ) )? > |
| { |
| inDirective = false; |
| stateStackPop(); |
| } |
| |
| | <IF_DIRECTIVE: "if"> |
| { |
| SwitchTo(DIRECTIVE); |
| } |
| |
| | <ELSEIF_DIRECTIVE: "elseif"> |
| { |
| SwitchTo(DIRECTIVE); |
| } |
| |
| | <ELSE_DIRECTIVE: "else" ( ( " " | "\t" )* ( "\n" | "\r" | "\r\n" ) )? > |
| { |
| inDirective = false; |
| stateStackPop(); |
| } |
| |
| | <STOP_DIRECTIVE: "stop"> |
| { |
| matchedToken.kind = EOF; |
| fileDepth = 0; |
| } |
| } |
| |
| <PRE_DIRECTIVE,DIRECTIVE,REFMOD2> |
| TOKEN: |
| { |
| <#DIGIT: [ "0"-"9" ] > |
| | <NUMBER_LITERAL: ("-")? (<DIGIT>)+ > |
| { |
| /* |
| * check to see if we are in set |
| * ex. #set $foo = $foo + 3 |
| * because we want to handle the \n after |
| */ |
| |
| if ( lparen == 0 && !inSet && curLexState != REFMOD2) |
| { |
| stateStackPop(); |
| } |
| |
| } |
| } |
| |
| <PRE_DIRECTIVE,DIRECTIVE> |
| TOKEN: |
| { |
| <#LETTER: [ "a"-"z", "A" - "Z" ] > |
| | <#DIRECTIVE_CHAR: [ "a"-"z", "A"-"Z", "0"-"9", "_" ] > |
| | <WORD: ( <LETTER> | ["_"]) (<DIRECTIVE_CHAR>)* > |
| |
| } |
| |
| /* ----------------------------------------------------------------------- |
| * |
| * REFERENCE Lexical States |
| * |
| * This is more than a single state, because of the structure of |
| * the VTL references. We use three states because the set of tokens |
| * for each state can be different. |
| * |
| * $foo.bar( "arg" ) |
| * ^ ^ ^ |
| * | | | |
| * ----------- > REFERENCE : state initiated by the '$' character. Continues |
| * | | until end of the reference, or the . character. |
| * |------ > REFMODIFIER : state switched to when the <DOT> is encountered. |
| * | note that this is a switch, not a push. See notes at bottom |
| * | re stateStack. |
| * |-- > REFMOD2 : state switch to when the LPAREN is encountered. |
| * again, this is a switch, not a push. |
| * |
| * ---------------------------------------------------------------------------- */ |
| |
| <REFERENCE,REFMODIFIER,REFMOD2> |
| TOKEN : |
| { |
| <#ALPHA_CHAR: ["a"-"z", "A"-"Z"] > |
| | <#ALPHANUM_CHAR: [ "a"-"z", "A"-"Z", "0"-"9" ] > |
| | <#IDENTIFIER_CHAR: [ "a"-"z", "A"-"Z", "0"-"9", "-", "_" ] > |
| | <IDENTIFIER: ( <ALPHA_CHAR> | ["_"]) (<IDENTIFIER_CHAR>)* > |
| | <DOT: "." <ALPHA_CHAR>> |
| { |
| /* |
| * push the alpha char back into the stream so the following identifier |
| * is complete |
| */ |
| |
| input_stream.backup(1); |
| |
| /* |
| * and munge the <DOT> so we just get a . when we have normal text that |
| * looks like a ref.ident |
| */ |
| |
| matchedToken.image = "."; |
| |
| if ( debugPrint ) |
| System.out.print("DOT : switching to " + REFMODIFIER); |
| SwitchTo(REFMODIFIER); |
| |
| } |
| | <LCURLY: "{"> |
| | <RCURLY: "}"> |
| { |
| stateStackPop(); |
| } |
| } |
| |
| <REFERENCE,REFMODIFIER,REFMOD2> |
| SPECIAL_TOKEN : |
| { |
| <REFERENCE_TERMINATOR: ~[] > |
| { |
| /* |
| * push every terminator character back into the stream |
| */ |
| |
| input_stream.backup(1); |
| |
| inReference = false; |
| |
| if ( debugPrint ) |
| System.out.print("REF_TERM :"); |
| |
| stateStackPop(); |
| } |
| } |
| |
| <PRE_DIRECTIVE> |
| SPECIAL_TOKEN : |
| { |
| <DIRECTIVE_TERMINATOR: ~[] > |
| { |
| if ( debugPrint ) |
| System.out.print("DIRECTIVE_TERM :"); |
| |
| input_stream.backup(1); |
| inDirective = false; |
| stateStackPop(); |
| } |
| } |
| |
| /** |
| * This method is what starts the whole parsing |
| * process. After the parsing is complete and |
| * the template has been turned into an AST, |
| * this method returns the root of AST which |
| * can subsequently be traversed by a visitor |
| * which implements the ParserVisitor interface |
| * which is generated automatically by JavaCC |
| */ |
| SimpleNode process() : {/*@bgen(jjtree) process */ |
| ASTprocess jjtn000 = new ASTprocess(this, JJTPROCESS); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) process */ |
| try { |
| /*@egen*/ |
| ( Statement() )* <EOF>/*@bgen(jjtree)*/ |
| { |
| jjtree.closeNodeScope(jjtn000, true); |
| jjtc000 = false; |
| } |
| /*@egen*/ |
| { return jjtn000; }/*@bgen(jjtree)*/ |
| } catch (Throwable jjte000) { |
| if (jjtc000) { |
| jjtree.clearNodeScope(jjtn000); |
| jjtc000 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte000 instanceof RuntimeException) { |
| throw (RuntimeException)jjte000; |
| } |
| if (jjte000 instanceof ParseException) { |
| throw (ParseException)jjte000; |
| } |
| throw (Error)jjte000; |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| } |
| |
| /** |
| * These are the types of statements that |
| * are acceptable in Velocity templates. |
| */ |
| void Statement() : {} |
| { |
| IfStatement() |
| | StopStatement() |
| | Reference() |
| | Comment() |
| | SetDirective() |
| | EscapedDirective() |
| | Escape() |
| | Directive() |
| | Text() |
| } |
| |
| /** |
| * used to separate the notion of a valid directive that has been |
| * escaped, versus something that looks like a directive and |
| * is just schmoo. This is important to do as a separate production |
| * that creates a node, because we want this, in either case, to stop |
| * the further parsing of the Directive() tree. |
| */ |
| void EscapedDirective() : {/*@bgen(jjtree) EscapedDirective */ |
| ASTEscapedDirective jjtn000 = new ASTEscapedDirective(this, JJTESCAPEDDIRECTIVE); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) EscapedDirective */ |
| try { |
| /*@egen*/ |
| { |
| Token t = null; |
| } |
| |
| t = <ESCAPE_DIRECTIVE>/*@bgen(jjtree)*/ |
| { |
| jjtree.closeNodeScope(jjtn000, true); |
| jjtc000 = false; |
| } |
| /*@egen*/ |
| { |
| /* |
| * churn and burn.. |
| */ |
| t.image = escapedDirective( t.image ); |
| }/*@bgen(jjtree)*/ |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| } |
| |
| /** |
| * Used to catch and process escape sequences in grammatical constructs |
| * as escapes outside of VTL are just characters. Right now we have both |
| * this and the EscapeDirective() construction because in the EscapeDirective() |
| * case, we want to suck in the #<directive> and here we don't. We just want |
| * the escapes to render correctly |
| */ |
| void Escape() : {/*@bgen(jjtree) Escape */ |
| ASTEscape jjtn000 = new ASTEscape(this, JJTESCAPE); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) Escape */ |
| try { |
| /*@egen*/ |
| { |
| Token t = null; |
| int count = 0; |
| boolean control = false; |
| } |
| |
| ( LOOKAHEAD(2) t = <DOUBLE_ESCAPE> |
| { |
| count++; |
| } |
| )+/*@bgen(jjtree)*/ |
| { |
| jjtree.closeNodeScope(jjtn000, true); |
| jjtc000 = false; |
| } |
| /*@egen*/ |
| { |
| /* |
| * first, check to see if we have a control directive |
| */ |
| switch(t.next.kind ) { |
| case IF_DIRECTIVE : |
| case ELSE_DIRECTIVE : |
| case ELSEIF_DIRECTIVE : |
| case END : |
| case STOP_DIRECTIVE : |
| control = true; |
| break; |
| } |
| |
| /* |
| * if that failed, lets lookahead to see if we matched a PD or a VM |
| */ |
| |
| if ( isDirective( t.next.image.substring(1))) |
| control = true; |
| else if ( Runtime.isVelocimacro( t.next.image.substring(1), currentTemplateName)) |
| control = true; |
| |
| t.image = ""; |
| |
| for( int i = 0; i < count; i++) |
| t.image += ( control ? "\\" : "\\\\"); |
| }/*@bgen(jjtree)*/ |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| |
| } |
| |
| void Comment() : {/*@bgen(jjtree) Comment */ |
| ASTComment jjtn000 = new ASTComment(this, JJTCOMMENT); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) Comment */ |
| try { |
| /*@egen*/ |
| <SINGLE_LINE_COMMENT> |
| | <MULTI_LINE_COMMENT> |
| | <FORMAL_COMMENT>/*@bgen(jjtree)*/ |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| } |
| |
| void NumberLiteral() : {/*@bgen(jjtree) NumberLiteral */ |
| ASTNumberLiteral jjtn000 = new ASTNumberLiteral(this, JJTNUMBERLITERAL); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) NumberLiteral */ |
| try { |
| /*@egen*/ |
| <NUMBER_LITERAL>/*@bgen(jjtree)*/ |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| } |
| |
| void StringLiteral() : {/*@bgen(jjtree) StringLiteral */ |
| ASTStringLiteral jjtn000 = new ASTStringLiteral(this, JJTSTRINGLITERAL); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) StringLiteral */ |
| try { |
| /*@egen*/ |
| <STRING_LITERAL>/*@bgen(jjtree)*/ |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| } |
| |
| /** |
| * This method corresponds to variable |
| * references in Velocity templates. |
| * The following are examples of variable |
| * references that may be found in a |
| * template: |
| * |
| * $foo |
| * $bar |
| * |
| */ |
| void Identifier() : {/*@bgen(jjtree) Identifier */ |
| ASTIdentifier jjtn000 = new ASTIdentifier(this, JJTIDENTIFIER); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) Identifier */ |
| try { |
| /*@egen*/ |
| <IDENTIFIER>/*@bgen(jjtree)*/ |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| } |
| |
| void Word() : {/*@bgen(jjtree) Word */ |
| ASTWord jjtn000 = new ASTWord(this, JJTWORD); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) Word */ |
| try { |
| /*@egen*/ |
| <WORD>/*@bgen(jjtree)*/ |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| } |
| |
| /** |
| * Supports the arguments for the Pluggable Directives |
| * We add whitespace in here as a token so the VMs can |
| * easily reconstruct a macro body from the token stream |
| * See Directive() |
| */ |
| void DirectiveArg() : {} |
| { |
| Reference() |
| | Word() |
| | StringLiteral() |
| | NumberLiteral() |
| | LOOKAHEAD(4) IntegerRange() |
| | ObjectArray() |
| | True() |
| | False() |
| | <WHITESPACE> |
| } |
| |
| /** |
| * Supports the Pluggable Directives |
| * #foo( arg+ ) |
| */ |
| SimpleNode Directive() : |
| {/*@bgen(jjtree) Directive */ |
| ASTDirective jjtn000 = new ASTDirective(this, JJTDIRECTIVE); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/ |
| Token t = null; |
| Directive d; |
| boolean doItNow = false; |
| } |
| {/*@bgen(jjtree) Directive */ |
| try { |
| /*@egen*/ |
| /* |
| * 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 |
| */ |
| |
| jjtn000.setDirectiveName( directiveName ); |
| |
| if ( d == null) |
| { |
| /* |
| * if null, then not a real directive, but maybe a Velocimacro |
| */ |
| |
| d = (Directive) Runtime.getVelocimacro( directiveName, currentTemplateName ); |
| |
| if (d == null) |
| { |
| token_source.stateStackPop(); |
| token_source.inDirective = false; |
| return jjtn000; |
| } |
| } |
| |
| /* |
| * now, switch us out of PRE_DIRECTIVE |
| */ |
| |
| token_source.SwitchTo(DIRECTIVE); |
| } |
| |
| /* |
| * if this is indeed a token, match the #foo ( arg ) pattern |
| */ |
| |
| [<WHITESPACE>] <LPAREN> ( DirectiveArg() )* <RPAREN> |
| { |
| if (d.getType() == Directive.LINE) |
| return jjtn000; |
| }/*@bgen(jjtree) Block */ |
| { |
| ASTBlock jjtn001 = new ASTBlock(this, JJTBLOCK); |
| boolean jjtc001 = true; |
| jjtree.openNodeScope(jjtn001); |
| } |
| try { |
| /*@egen*/ |
| |
| /* |
| * and the following block if the PD needs it |
| */ |
| |
| ( Statement() )+/*@bgen(jjtree)*/ |
| } catch (Throwable jjte001) { |
| if (jjtc001) { |
| jjtree.clearNodeScope(jjtn001); |
| jjtc001 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte001 instanceof RuntimeException) { |
| throw (RuntimeException)jjte001; |
| } |
| if (jjte001 instanceof ParseException) { |
| throw (ParseException)jjte001; |
| } |
| throw (Error)jjte001; |
| } finally { |
| if (jjtc001) { |
| jjtree.closeNodeScope(jjtn001, true); |
| } |
| } |
| /*@egen*/ |
| <END>/*@bgen(jjtree)*/ |
| { |
| jjtree.closeNodeScope(jjtn000, true); |
| jjtc000 = false; |
| } |
| /*@egen*/ |
| { |
| /* |
| * 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 m = new Macro(); |
| m.processAndRegister( jjtn000, currentTemplateName ); |
| } |
| |
| /* |
| * VM : end |
| */ |
| |
| return jjtn000; |
| }/*@bgen(jjtree)*/ |
| } catch (Throwable jjte000) { |
| if (jjtc000) { |
| jjtree.clearNodeScope(jjtn000); |
| jjtc000 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte000 instanceof RuntimeException) { |
| throw (RuntimeException)jjte000; |
| } |
| if (jjte000 instanceof ParseException) { |
| throw (ParseException)jjte000; |
| } |
| throw (Error)jjte000; |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| } |
| |
| void ObjectArray() : {/*@bgen(jjtree) ObjectArray */ |
| ASTObjectArray jjtn000 = new ASTObjectArray(this, JJTOBJECTARRAY); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) ObjectArray */ |
| try { |
| /*@egen*/ |
| <LBRACKET> [ Parameter() ( <COMMA> Parameter() )* ] <RBRACKET>/*@bgen(jjtree)*/ |
| } catch (Throwable jjte000) { |
| if (jjtc000) { |
| jjtree.clearNodeScope(jjtn000); |
| jjtc000 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte000 instanceof RuntimeException) { |
| throw (RuntimeException)jjte000; |
| } |
| if (jjte000 instanceof ParseException) { |
| throw (ParseException)jjte000; |
| } |
| throw (Error)jjte000; |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| } |
| |
| /** |
| * 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() : {/*@bgen(jjtree) IntegerRange */ |
| ASTIntegerRange jjtn000 = new ASTIntegerRange(this, JJTINTEGERRANGE); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) IntegerRange */ |
| try { |
| /*@egen*/ |
| <LBRACKET> [<WHITESPACE>] |
| ( Reference() | NumberLiteral()) |
| [<WHITESPACE>] <DOUBLEDOT> [<WHITESPACE>] |
| (Reference() | NumberLiteral()) |
| [<WHITESPACE>] <RBRACKET>/*@bgen(jjtree)*/ |
| } catch (Throwable jjte000) { |
| if (jjtc000) { |
| jjtree.clearNodeScope(jjtn000); |
| jjtc000 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte000 instanceof RuntimeException) { |
| throw (RuntimeException)jjte000; |
| } |
| if (jjte000 instanceof ParseException) { |
| throw (ParseException)jjte000; |
| } |
| throw (Error)jjte000; |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| } |
| |
| |
| /** |
| * This method has yet to be fully implemented |
| * but will allow arbitrarily nested method |
| * calls |
| */ |
| void Parameter() : {} |
| { |
| [<WHITESPACE>] |
| ( |
| StringLiteral() |
| | ObjectArray() |
| | True() |
| | False() |
| | Reference() |
| | NumberLiteral() |
| ) |
| [<WHITESPACE>] |
| } |
| |
| /** |
| * This method has yet to be fully implemented |
| * but will allow arbitrarily nested method |
| * calls |
| */ |
| void Method() : {/*@bgen(jjtree) Method */ |
| ASTMethod jjtn000 = new ASTMethod(this, JJTMETHOD); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) Method */ |
| try { |
| /*@egen*/ |
| Identifier() <LPAREN> [ Parameter() ( <COMMA> Parameter() )* ] <REFMOD2_RPAREN>/*@bgen(jjtree)*/ |
| } catch (Throwable jjte000) { |
| if (jjtc000) { |
| jjtree.clearNodeScope(jjtn000); |
| jjtc000 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte000 instanceof RuntimeException) { |
| throw (RuntimeException)jjte000; |
| } |
| if (jjte000 instanceof ParseException) { |
| throw (ParseException)jjte000; |
| } |
| throw (Error)jjte000; |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| } |
| |
| void Reference() : {/*@bgen(jjtree) Reference */ |
| ASTReference jjtn000 = new ASTReference(this, JJTREFERENCE); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) Reference */ |
| try { |
| /*@egen*/ |
| // This should be changed to Indentifier() now. Make |
| // it easier to walk the AST. |
| [<LCURLY>] |
| <IDENTIFIER> |
| [<RCURLY>] |
| (LOOKAHEAD(2) <DOT> (LOOKAHEAD(3) Method() | Identifier() ) [<RCURLY>] )*/*@bgen(jjtree)*/ |
| } catch (Throwable jjte000) { |
| if (jjtc000) { |
| jjtree.clearNodeScope(jjtn000); |
| jjtc000 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte000 instanceof RuntimeException) { |
| throw (RuntimeException)jjte000; |
| } |
| if (jjte000 instanceof ParseException) { |
| throw (ParseException)jjte000; |
| } |
| throw (Error)jjte000; |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| } |
| |
| void True() : {/*@bgen(jjtree) True */ |
| ASTTrue jjtn000 = new ASTTrue(this, JJTTRUE); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) True */ |
| try { |
| /*@egen*/ |
| <TRUE>/*@bgen(jjtree)*/ |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| } |
| |
| void False() : {/*@bgen(jjtree) False */ |
| ASTFalse jjtn000 = new ASTFalse(this, JJTFALSE); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) False */ |
| try { |
| /*@egen*/ |
| <FALSE>/*@bgen(jjtree)*/ |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| } |
| |
| /** |
| * This method is responsible for allowing |
| * all non-grammar text to pass through |
| * unscathed. |
| */ |
| void Text() : {/*@bgen(jjtree) Text */ |
| ASTText jjtn000 = new ASTText(this, JJTTEXT); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) Text */ |
| try { |
| /*@egen*/ |
| <TEXT> |
| | <DOT> |
| | <RPAREN> |
| | <LPAREN> |
| | <NUMBER_LITERAL> |
| | <STRING_LITERAL> |
| | <ESCAPE>/*@bgen(jjtree)*/ |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| } |
| |
| /* ----------------------------------------------------------------------- |
| * |
| * Defined Directive Syntax |
| * |
| * ----------------------------------------------------------------------*/ |
| |
| void IfStatement() : {/*@bgen(jjtree) IfStatement */ |
| ASTIfStatement jjtn000 = new ASTIfStatement(this, JJTIFSTATEMENT); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) IfStatement */ |
| try { |
| /*@egen*/ |
| <IF_DIRECTIVE> [<WHITESPACE>] <LPAREN> Expression() <RPAREN>/*@bgen(jjtree) Block */ |
| { |
| ASTBlock jjtn001 = new ASTBlock(this, JJTBLOCK); |
| boolean jjtc001 = true; |
| jjtree.openNodeScope(jjtn001); |
| } |
| try { |
| /*@egen*/ |
| ( Statement() )+/*@bgen(jjtree)*/ |
| } catch (Throwable jjte001) { |
| if (jjtc001) { |
| jjtree.clearNodeScope(jjtn001); |
| jjtc001 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte001 instanceof RuntimeException) { |
| throw (RuntimeException)jjte001; |
| } |
| if (jjte001 instanceof ParseException) { |
| throw (ParseException)jjte001; |
| } |
| throw (Error)jjte001; |
| } finally { |
| if (jjtc001) { |
| jjtree.closeNodeScope(jjtn001, true); |
| } |
| } |
| /*@egen*/ |
| [ LOOKAHEAD(1) ( ElseIfStatement() )+ ] |
| [ LOOKAHEAD(1) ElseStatement() ] |
| <END>/*@bgen(jjtree)*/ |
| } catch (Throwable jjte000) { |
| if (jjtc000) { |
| jjtree.clearNodeScope(jjtn000); |
| jjtc000 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte000 instanceof RuntimeException) { |
| throw (RuntimeException)jjte000; |
| } |
| if (jjte000 instanceof ParseException) { |
| throw (ParseException)jjte000; |
| } |
| throw (Error)jjte000; |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| |
| } |
| |
| void ElseStatement() : {/*@bgen(jjtree) ElseStatement */ |
| ASTElseStatement jjtn000 = new ASTElseStatement(this, JJTELSESTATEMENT); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) ElseStatement */ |
| try { |
| /*@egen*/ |
| <ELSE_DIRECTIVE>/*@bgen(jjtree) Block */ |
| { |
| ASTBlock jjtn001 = new ASTBlock(this, JJTBLOCK); |
| boolean jjtc001 = true; |
| jjtree.openNodeScope(jjtn001); |
| } |
| try { |
| /*@egen*/ |
| ( Statement() )+/*@bgen(jjtree)*/ |
| } catch (Throwable jjte001) { |
| if (jjtc001) { |
| jjtree.clearNodeScope(jjtn001); |
| jjtc001 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte001 instanceof RuntimeException) { |
| throw (RuntimeException)jjte001; |
| } |
| if (jjte001 instanceof ParseException) { |
| throw (ParseException)jjte001; |
| } |
| throw (Error)jjte001; |
| } finally { |
| if (jjtc001) { |
| jjtree.closeNodeScope(jjtn001, true); |
| } |
| } |
| /*@egen*//*@bgen(jjtree)*/ |
| } catch (Throwable jjte000) { |
| if (jjtc000) { |
| jjtree.clearNodeScope(jjtn000); |
| jjtc000 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte000 instanceof RuntimeException) { |
| throw (RuntimeException)jjte000; |
| } |
| if (jjte000 instanceof ParseException) { |
| throw (ParseException)jjte000; |
| } |
| throw (Error)jjte000; |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| } |
| |
| void ElseIfStatement() : {/*@bgen(jjtree) ElseIfStatement */ |
| ASTElseIfStatement jjtn000 = new ASTElseIfStatement(this, JJTELSEIFSTATEMENT); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) ElseIfStatement */ |
| try { |
| /*@egen*/ |
| <ELSEIF_DIRECTIVE> [<WHITESPACE>] |
| <LPAREN> Expression() <RPAREN>/*@bgen(jjtree) Block */ |
| { |
| ASTBlock jjtn001 = new ASTBlock(this, JJTBLOCK); |
| boolean jjtc001 = true; |
| jjtree.openNodeScope(jjtn001); |
| } |
| try { |
| /*@egen*/ |
| ( Statement() )+/*@bgen(jjtree)*/ |
| } catch (Throwable jjte001) { |
| if (jjtc001) { |
| jjtree.clearNodeScope(jjtn001); |
| jjtc001 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte001 instanceof RuntimeException) { |
| throw (RuntimeException)jjte001; |
| } |
| if (jjte001 instanceof ParseException) { |
| throw (ParseException)jjte001; |
| } |
| throw (Error)jjte001; |
| } finally { |
| if (jjtc001) { |
| jjtree.closeNodeScope(jjtn001, true); |
| } |
| } |
| /*@egen*//*@bgen(jjtree)*/ |
| } catch (Throwable jjte000) { |
| if (jjtc000) { |
| jjtree.clearNodeScope(jjtn000); |
| jjtc000 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte000 instanceof RuntimeException) { |
| throw (RuntimeException)jjte000; |
| } |
| if (jjte000 instanceof ParseException) { |
| throw (ParseException)jjte000; |
| } |
| throw (Error)jjte000; |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| } |
| |
| /** |
| * Currently support both types of set : |
| * #set( expr ) |
| * #set expr |
| */ |
| void SetDirective() : {/*@bgen(jjtree) SetDirective */ |
| ASTSetDirective jjtn000 = new ASTSetDirective(this, JJTSETDIRECTIVE); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) SetDirective */ |
| try { |
| /*@egen*/ |
| <SET_DIRECTIVE> |
| [ LOOKAHEAD(2) <WHITESPACE> ] |
| ( <LPAREN> Expression() <RPAREN> |
| { |
| /* |
| * ensure that inSet is false. Leads to some amusing bugs... |
| */ |
| |
| token_source.inSet = false; |
| } |
| [<NEWLINE>] )/*@bgen(jjtree)*/ |
| } catch (Throwable jjte000) { |
| if (jjtc000) { |
| jjtree.clearNodeScope(jjtn000); |
| jjtc000 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte000 instanceof RuntimeException) { |
| throw (RuntimeException)jjte000; |
| } |
| if (jjte000 instanceof ParseException) { |
| throw (ParseException)jjte000; |
| } |
| throw (Error)jjte000; |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| } |
| |
| /** |
| * 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() : {} |
| { |
| <STOP_DIRECTIVE> |
| } |
| |
| /* ----------------------------------------------------------------------- |
| * |
| * Expression Syntax |
| * |
| * ----------------------------------------------------------------------*/ |
| |
| void Expression() : {/*@bgen(jjtree) Expression */ |
| ASTExpression jjtn000 = new ASTExpression(this, JJTEXPRESSION); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) Expression */ |
| try { |
| /*@egen*/ |
| LOOKAHEAD( PrimaryExpression() <EQUALS> ) Assignment() |
| | ConditionalOrExpression()/*@bgen(jjtree)*/ |
| } catch (Throwable jjte000) { |
| if (jjtc000) { |
| jjtree.clearNodeScope(jjtn000); |
| jjtc000 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte000 instanceof RuntimeException) { |
| throw (RuntimeException)jjte000; |
| } |
| if (jjte000 instanceof ParseException) { |
| throw (ParseException)jjte000; |
| } |
| throw (Error)jjte000; |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, true); |
| } |
| } |
| /*@egen*/ |
| } |
| |
| void Assignment() : {/*@bgen(jjtree) #Assignment( 2) */ |
| ASTAssignment jjtn000 = new ASTAssignment(this, JJTASSIGNMENT); |
| boolean jjtc000 = true; |
| jjtree.openNodeScope(jjtn000); |
| /*@egen*/} |
| {/*@bgen(jjtree) #Assignment( 2) */ |
| try { |
| /*@egen*/ |
| PrimaryExpression() <EQUALS> Expression()/*@bgen(jjtree)*/ |
| } catch (Throwable jjte000) { |
| if (jjtc000) { |
| jjtree.clearNodeScope(jjtn000); |
| jjtc000 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte000 instanceof RuntimeException) { |
| throw (RuntimeException)jjte000; |
| } |
| if (jjte000 instanceof ParseException) { |
| throw (ParseException)jjte000; |
| } |
| throw (Error)jjte000; |
| } finally { |
| if (jjtc000) { |
| jjtree.closeNodeScope(jjtn000, 2); |
| } |
| } |
| /*@egen*/ |
| } |
| |
| void ConditionalOrExpression() : {} |
| { |
| ConditionalAndExpression() |
| ( <LOGICAL_OR>/*@bgen(jjtree) #OrNode( 2) */ |
| { |
| ASTOrNode jjtn001 = new ASTOrNode(this, JJTORNODE); |
| boolean jjtc001 = true; |
| jjtree.openNodeScope(jjtn001); |
| } |
| try { |
| /*@egen*/ ConditionalAndExpression()/*@bgen(jjtree)*/ |
| } catch (Throwable jjte001) { |
| if (jjtc001) { |
| jjtree.clearNodeScope(jjtn001); |
| jjtc001 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte001 instanceof RuntimeException) { |
| throw (RuntimeException)jjte001; |
| } |
| if (jjte001 instanceof ParseException) { |
| throw (ParseException)jjte001; |
| } |
| throw (Error)jjte001; |
| } finally { |
| if (jjtc001) { |
| jjtree.closeNodeScope(jjtn001, 2); |
| } |
| } |
| /*@egen*/ )* |
| } |
| |
| |
| void ConditionalAndExpression() : {} |
| { |
| EqualityExpression() |
| ( <LOGICAL_AND>/*@bgen(jjtree) #AndNode( 2) */ |
| { |
| ASTAndNode jjtn001 = new ASTAndNode(this, JJTANDNODE); |
| boolean jjtc001 = true; |
| jjtree.openNodeScope(jjtn001); |
| } |
| try { |
| /*@egen*/ EqualityExpression()/*@bgen(jjtree)*/ |
| } catch (Throwable jjte001) { |
| if (jjtc001) { |
| jjtree.clearNodeScope(jjtn001); |
| jjtc001 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte001 instanceof RuntimeException) { |
| throw (RuntimeException)jjte001; |
| } |
| if (jjte001 instanceof ParseException) { |
| throw (ParseException)jjte001; |
| } |
| throw (Error)jjte001; |
| } finally { |
| if (jjtc001) { |
| jjtree.closeNodeScope(jjtn001, 2); |
| } |
| } |
| /*@egen*/ )* |
| } |
| |
| void EqualityExpression() : {} |
| { |
| RelationalExpression() |
| ( |
| <LOGICAL_EQUALS>/*@bgen(jjtree) #EQNode( 2) */ |
| { |
| ASTEQNode jjtn001 = new ASTEQNode(this, JJTEQNODE); |
| boolean jjtc001 = true; |
| jjtree.openNodeScope(jjtn001); |
| } |
| try { |
| /*@egen*/ RelationalExpression()/*@bgen(jjtree)*/ |
| } catch (Throwable jjte001) { |
| if (jjtc001) { |
| jjtree.clearNodeScope(jjtn001); |
| jjtc001 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte001 instanceof RuntimeException) { |
| throw (RuntimeException)jjte001; |
| } |
| if (jjte001 instanceof ParseException) { |
| throw (ParseException)jjte001; |
| } |
| throw (Error)jjte001; |
| } finally { |
| if (jjtc001) { |
| jjtree.closeNodeScope(jjtn001, 2); |
| } |
| } |
| /*@egen*/ |
| | <LOGICAL_NOT_EQUALS>/*@bgen(jjtree) #NENode( 2) */ |
| { |
| ASTNENode jjtn002 = new ASTNENode(this, JJTNENODE); |
| boolean jjtc002 = true; |
| jjtree.openNodeScope(jjtn002); |
| } |
| try { |
| /*@egen*/ RelationalExpression()/*@bgen(jjtree)*/ |
| } catch (Throwable jjte002) { |
| if (jjtc002) { |
| jjtree.clearNodeScope(jjtn002); |
| jjtc002 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte002 instanceof RuntimeException) { |
| throw (RuntimeException)jjte002; |
| } |
| if (jjte002 instanceof ParseException) { |
| throw (ParseException)jjte002; |
| } |
| throw (Error)jjte002; |
| } finally { |
| if (jjtc002) { |
| jjtree.closeNodeScope(jjtn002, 2); |
| } |
| } |
| /*@egen*/ |
| )* |
| } |
| |
| void RelationalExpression() : {} |
| { |
| AdditiveExpression() |
| ( |
| <LOGICAL_LT>/*@bgen(jjtree) #LTNode( 2) */ |
| { |
| ASTLTNode jjtn001 = new ASTLTNode(this, JJTLTNODE); |
| boolean jjtc001 = true; |
| jjtree.openNodeScope(jjtn001); |
| } |
| try { |
| /*@egen*/ AdditiveExpression()/*@bgen(jjtree)*/ |
| } catch (Throwable jjte001) { |
| if (jjtc001) { |
| jjtree.clearNodeScope(jjtn001); |
| jjtc001 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte001 instanceof RuntimeException) { |
| throw (RuntimeException)jjte001; |
| } |
| if (jjte001 instanceof ParseException) { |
| throw (ParseException)jjte001; |
| } |
| throw (Error)jjte001; |
| } finally { |
| if (jjtc001) { |
| jjtree.closeNodeScope(jjtn001, 2); |
| } |
| } |
| /*@egen*/ |
| | <LOGICAL_GT>/*@bgen(jjtree) #GTNode( 2) */ |
| { |
| ASTGTNode jjtn002 = new ASTGTNode(this, JJTGTNODE); |
| boolean jjtc002 = true; |
| jjtree.openNodeScope(jjtn002); |
| } |
| try { |
| /*@egen*/ AdditiveExpression()/*@bgen(jjtree)*/ |
| } catch (Throwable jjte002) { |
| if (jjtc002) { |
| jjtree.clearNodeScope(jjtn002); |
| jjtc002 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte002 instanceof RuntimeException) { |
| throw (RuntimeException)jjte002; |
| } |
| if (jjte002 instanceof ParseException) { |
| throw (ParseException)jjte002; |
| } |
| throw (Error)jjte002; |
| } finally { |
| if (jjtc002) { |
| jjtree.closeNodeScope(jjtn002, 2); |
| } |
| } |
| /*@egen*/ |
| | <LOGICAL_LE>/*@bgen(jjtree) #LENode( 2) */ |
| { |
| ASTLENode jjtn003 = new ASTLENode(this, JJTLENODE); |
| boolean jjtc003 = true; |
| jjtree.openNodeScope(jjtn003); |
| } |
| try { |
| /*@egen*/ AdditiveExpression()/*@bgen(jjtree)*/ |
| } catch (Throwable jjte003) { |
| if (jjtc003) { |
| jjtree.clearNodeScope(jjtn003); |
| jjtc003 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte003 instanceof RuntimeException) { |
| throw (RuntimeException)jjte003; |
| } |
| if (jjte003 instanceof ParseException) { |
| throw (ParseException)jjte003; |
| } |
| throw (Error)jjte003; |
| } finally { |
| if (jjtc003) { |
| jjtree.closeNodeScope(jjtn003, 2); |
| } |
| } |
| /*@egen*/ |
| | <LOGICAL_GE>/*@bgen(jjtree) #GENode( 2) */ |
| { |
| ASTGENode jjtn004 = new ASTGENode(this, JJTGENODE); |
| boolean jjtc004 = true; |
| jjtree.openNodeScope(jjtn004); |
| } |
| try { |
| /*@egen*/ AdditiveExpression()/*@bgen(jjtree)*/ |
| } catch (Throwable jjte004) { |
| if (jjtc004) { |
| jjtree.clearNodeScope(jjtn004); |
| jjtc004 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte004 instanceof RuntimeException) { |
| throw (RuntimeException)jjte004; |
| } |
| if (jjte004 instanceof ParseException) { |
| throw (ParseException)jjte004; |
| } |
| throw (Error)jjte004; |
| } finally { |
| if (jjtc004) { |
| jjtree.closeNodeScope(jjtn004, 2); |
| } |
| } |
| /*@egen*/ |
| )* |
| } |
| |
| void AdditiveExpression() : {} |
| { |
| MultiplicativeExpression() |
| ( |
| <PLUS>/*@bgen(jjtree) #AddNode( 2) */ |
| { |
| ASTAddNode jjtn001 = new ASTAddNode(this, JJTADDNODE); |
| boolean jjtc001 = true; |
| jjtree.openNodeScope(jjtn001); |
| } |
| try { |
| /*@egen*/ MultiplicativeExpression()/*@bgen(jjtree)*/ |
| } catch (Throwable jjte001) { |
| if (jjtc001) { |
| jjtree.clearNodeScope(jjtn001); |
| jjtc001 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte001 instanceof RuntimeException) { |
| throw (RuntimeException)jjte001; |
| } |
| if (jjte001 instanceof ParseException) { |
| throw (ParseException)jjte001; |
| } |
| throw (Error)jjte001; |
| } finally { |
| if (jjtc001) { |
| jjtree.closeNodeScope(jjtn001, 2); |
| } |
| } |
| /*@egen*/ |
| | <MINUS>/*@bgen(jjtree) #SubtractNode( 2) */ |
| { |
| ASTSubtractNode jjtn002 = new ASTSubtractNode(this, JJTSUBTRACTNODE); |
| boolean jjtc002 = true; |
| jjtree.openNodeScope(jjtn002); |
| } |
| try { |
| /*@egen*/ MultiplicativeExpression()/*@bgen(jjtree)*/ |
| } catch (Throwable jjte002) { |
| if (jjtc002) { |
| jjtree.clearNodeScope(jjtn002); |
| jjtc002 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte002 instanceof RuntimeException) { |
| throw (RuntimeException)jjte002; |
| } |
| if (jjte002 instanceof ParseException) { |
| throw (ParseException)jjte002; |
| } |
| throw (Error)jjte002; |
| } finally { |
| if (jjtc002) { |
| jjtree.closeNodeScope(jjtn002, 2); |
| } |
| } |
| /*@egen*/ |
| )* |
| } |
| |
| void MultiplicativeExpression() : {} |
| { |
| UnaryExpression() |
| ( |
| <MULTIPLY>/*@bgen(jjtree) #MulNode( 2) */ |
| { |
| ASTMulNode jjtn001 = new ASTMulNode(this, JJTMULNODE); |
| boolean jjtc001 = true; |
| jjtree.openNodeScope(jjtn001); |
| } |
| try { |
| /*@egen*/ UnaryExpression()/*@bgen(jjtree)*/ |
| } catch (Throwable jjte001) { |
| if (jjtc001) { |
| jjtree.clearNodeScope(jjtn001); |
| jjtc001 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte001 instanceof RuntimeException) { |
| throw (RuntimeException)jjte001; |
| } |
| if (jjte001 instanceof ParseException) { |
| throw (ParseException)jjte001; |
| } |
| throw (Error)jjte001; |
| } finally { |
| if (jjtc001) { |
| jjtree.closeNodeScope(jjtn001, 2); |
| } |
| } |
| /*@egen*/ |
| | <DIVIDE>/*@bgen(jjtree) #DivNode( 2) */ |
| { |
| ASTDivNode jjtn002 = new ASTDivNode(this, JJTDIVNODE); |
| boolean jjtc002 = true; |
| jjtree.openNodeScope(jjtn002); |
| } |
| try { |
| /*@egen*/ UnaryExpression()/*@bgen(jjtree)*/ |
| } catch (Throwable jjte002) { |
| if (jjtc002) { |
| jjtree.clearNodeScope(jjtn002); |
| jjtc002 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte002 instanceof RuntimeException) { |
| throw (RuntimeException)jjte002; |
| } |
| if (jjte002 instanceof ParseException) { |
| throw (ParseException)jjte002; |
| } |
| throw (Error)jjte002; |
| } finally { |
| if (jjtc002) { |
| jjtree.closeNodeScope(jjtn002, 2); |
| } |
| } |
| /*@egen*/ |
| | <MODULUS>/*@bgen(jjtree) #ModNode( 2) */ |
| { |
| ASTModNode jjtn003 = new ASTModNode(this, JJTMODNODE); |
| boolean jjtc003 = true; |
| jjtree.openNodeScope(jjtn003); |
| } |
| try { |
| /*@egen*/ UnaryExpression()/*@bgen(jjtree)*/ |
| } catch (Throwable jjte003) { |
| if (jjtc003) { |
| jjtree.clearNodeScope(jjtn003); |
| jjtc003 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte003 instanceof RuntimeException) { |
| throw (RuntimeException)jjte003; |
| } |
| if (jjte003 instanceof ParseException) { |
| throw (ParseException)jjte003; |
| } |
| throw (Error)jjte003; |
| } finally { |
| if (jjtc003) { |
| jjtree.closeNodeScope(jjtn003, 2); |
| } |
| } |
| /*@egen*/ |
| )* |
| } |
| |
| void UnaryExpression() : {} |
| { |
| LOOKAHEAD(2) [<WHITESPACE>] <LOGICAL_NOT>/*@bgen(jjtree) #NotNode( 1) */ |
| { |
| ASTNotNode jjtn001 = new ASTNotNode(this, JJTNOTNODE); |
| boolean jjtc001 = true; |
| jjtree.openNodeScope(jjtn001); |
| } |
| try { |
| /*@egen*/ UnaryExpression()/*@bgen(jjtree)*/ |
| } catch (Throwable jjte001) { |
| if (jjtc001) { |
| jjtree.clearNodeScope(jjtn001); |
| jjtc001 = false; |
| } else { |
| jjtree.popNode(); |
| } |
| if (jjte001 instanceof RuntimeException) { |
| throw (RuntimeException)jjte001; |
| } |
| if (jjte001 instanceof ParseException) { |
| throw (ParseException)jjte001; |
| } |
| throw (Error)jjte001; |
| } finally { |
| if (jjtc001) { |
| jjtree.closeNodeScope(jjtn001, 1); |
| } |
| } |
| /*@egen*/ |
| | PrimaryExpression() |
| } |
| |
| void PrimaryExpression() : {} |
| { |
| [<WHITESPACE>] |
| ( |
| StringLiteral() |
| | NumberLiteral() |
| | Reference() |
| | LOOKAHEAD(3) ObjectArray() |
| | IntegerRange() |
| | 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 |
| ------ |
| |
| */ |
| |