xref: /aosp_15_r20/external/apache-velocity-engine/velocity-engine-core/src/main/parser/Parser.jjt (revision 6626518d3d1a3a4cd21045b7974855fd6a3e2103)
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements.  See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership.  The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License.  You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied.  See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20/*
21 *  NOTE : please see documentation at bottom of this file. (It was placed there its tiring
22 *    to always have to page past it... :)
23 */
24options
25{
26    /** The default package for this parser kit. This is now done from Maven.
27    NODE_PACKAGE="org.apache.velocity.runtime.parser";
28    */
29
30    /** A source file will  be generated for each non-terminal */
31    MULTI=true;
32
33    /**
34     * Each node will have access to the parser, I did this so
35     * some global information can be shared via the parser. I
36     * think this will come in handly keeping track of
37     * context, and being able to push changes back into
38     * the context when nodes make modifications to the
39     * context by setting properties, variables and
40     * what not.
41     */
42    NODE_USES_PARSER=true;
43
44    /**
45     * The parser must be non-static in order for the
46     * above option to work, otherwise the parser value
47     * is passed in as null, which isn't all the useful ;)
48     */
49    STATIC=false;
50
51    /**
52     * Enables the use of a visitor that each of nodes
53     * will accept. This way we can separate the logic
54     * of node processing in a visitor and out of the
55     * nodes themselves. If processing changes then
56     * the nothing has to change in the node code.
57     */
58    VISITOR=true;
59
60    /**
61     * Declare that we are accepting unicode input and
62     * that we are using a custom character stream class
63     * Note that the char stream class is really a slightly
64     * modified ASCII_CharStream, as it appears we are safe
65     * because we only deal with pre-encoding-converted
66     * Readers rather than raw input streams.
67     */
68    UNICODE_INPUT=true;
69    USER_CHAR_STREAM=true;
70
71    /**
72     *  for debugging purposes. Those are now handled from within javacc-maven-plugin debugging flags in pom.xml
73    DEBUG_PARSER = true;
74    DEBUG_LOOKAHEAD = true;
75    DEBUG_TOKEN_MANAGER = true;
76     */
77
78
79}
80
81PARSER_BEGIN(${parser.basename}Parser)
82package ${parser.package};
83
84import java.io.*;
85import java.util.*;
86import org.apache.velocity.Template;
87import org.apache.velocity.exception.VelocityException;
88import org.apache.velocity.runtime.RuntimeServices;
89import org.apache.velocity.runtime.parser.*;
90import org.apache.velocity.runtime.parser.node.*;
91import org.apache.velocity.runtime.directive.*;
92import org.apache.velocity.runtime.directive.MacroParseException;
93import org.apache.velocity.runtime.RuntimeConstants;
94import static org.apache.velocity.runtime.RuntimeConstants.SpaceGobbling;
95
96import org.slf4j.Logger;
97
98/**
99 * This class is responsible for parsing a Velocity
100 * template. This class was generated by JavaCC using
101 * the JJTree extension to produce an Abstract
102 * Syntax Tree (AST) of the template.
103 *
104 * Please look at the Parser.jjt file which is
105 * what controls the generation of this class.
106 *
107 * @author <a href="mailto:[email protected]">Jason van Zyl</a>
108 * @author <a href="mailto:[email protected]">Geir Magnusson Jr.</a>
109 * @author <a href="[email protected]">Henning P. Schmiedehausen</a>
110 * @version $Id$
111*/
112public class ${parser.basename}Parser implements Parser
113{
114    /**
115     * Parser debugging flag.
116     * When debug is active, javacc Parser will contain (among other things)
117     * a trace_call() method. So we use the presence of this method to
118     * initialize our flag.
119     */
120    private static boolean debugParser;
121    static
122    {
123        try
124        {
125            ${parser.basename}Parser.class.getDeclaredMethod("trace_call", String.class);
126            debugParser = true;
127        }
128        catch(NoSuchMethodException nsfe)
129        {
130            debugParser = false;
131        }
132    }
133
134    /**
135     * Our own trace method. Use sparsingly in production, since each
136     * and every call will introduce an execution branch and slow down parsing.
137     */
138    public static void trace(String message)
139    {
140        if (debugParser) System.out.println(message);
141    }
142
143    /**
144     * Keep track of defined macros, used for escape processing
145     */
146    private Map macroNames = new HashMap();
147
148    /**
149     * Current template we are parsing.  Passed to us in parse()
150     */
151    public Template currentTemplate = null;
152
153    /**
154     * Set to true if the property
155     * RuntimeConstants.RUNTIME_REFERENCES_STRICT_ESCAPE is set to true
156     */
157    public boolean strictEscape = false;
158
159    /**
160     * Set to true if the propoerty
161     * RuntimeConstants.PARSER_HYPHEN_ALLOWED is set to true
162     */
163    public boolean hyphenAllowedInIdentifiers = false;
164
165    VelocityCharStream velcharstream = null;
166
167    private RuntimeServices rsvc = null;
168
169    @Override
170    public RuntimeServices getRuntimeServices()
171    {
172        return rsvc;
173    }
174
175    private Logger log = null;
176
177    /**
178     * This constructor was added to allow the re-use of parsers.
179     * The normal constructor takes a single argument which
180     * an InputStream. This simply creates a re-usable parser
181     * object, we satisfy the requirement of an InputStream
182     * by using a newline character as an input stream.
183     */
184    public ${parser.basename}Parser( RuntimeServices rs)
185    {
186        /*
187         * need to call the CTOR first thing.
188         */
189
190        this(   new VelocityCharStream(
191                new ByteArrayInputStream("\n".getBytes()), 1, 1 ));
192
193        /*
194         * then initialize logger
195         */
196
197         log = rs.getLog("parser");
198
199
200        /*
201         * now setup a VCS for later use
202         */
203        velcharstream = new VelocityCharStream(
204                new ByteArrayInputStream("\n".getBytes()), 1, 1 );
205
206
207        strictEscape =
208            rs.getBoolean(RuntimeConstants.RUNTIME_REFERENCES_STRICT_ESCAPE, false);
209
210        hyphenAllowedInIdentifiers =
211            rs.getBoolean(RuntimeConstants.PARSER_HYPHEN_ALLOWED, false);
212
213        /*
214         *  and save the RuntimeServices
215         */
216        rsvc = rs;
217
218        /*
219         * then initialize customizable characters
220         */
221         dollar   = '${parser.char.dollar}';
222         hash     = '${parser.char.hash}';
223         at       = '${parser.char.at}';
224         asterisk = '${parser.char.asterisk}';
225    }
226
227    /**
228     * This was also added to allow parsers to be
229     * re-usable. Normal JavaCC use entails passing an
230     * input stream to the constructor and the parsing
231     * process is carried out once. We want to be able
232     * to re-use parsers: we do this by adding this
233     * method and re-initializing the lexer with
234     * the new stream that we want parsed.
235     */
236    @Override
237    public SimpleNode parse( Reader reader, Template template )
238        throws ParseException
239    {
240        SimpleNode sn = null;
241
242        currentTemplate = template;
243
244        try
245        {
246            token_source.clearStateVars();
247
248            /*
249             *  reinitialize the VelocityCharStream
250             *  with the new reader
251             */
252            velcharstream.ReInit( reader, 1, 1 );
253
254            /*
255             * now reinit the Parser with this CharStream
256             */
257            ReInit( velcharstream  );
258
259            /*
260             *  do that voodoo...
261             */
262            sn = process();
263        }
264        catch (MacroParseException mee)
265        {
266            /*
267             *  thrown by the Macro class when something is amiss in the
268             *  Macro specification
269             */
270            log.error("{}: {}", template.getName(), mee.getMessage(), mee);
271            throw mee;
272        }
273        catch (ParseException pe)
274        {
275            log.error("{}: {}", currentTemplate.getName(), pe.getMessage());
276            throw new TemplateParseException (pe.currentToken,
277				pe.expectedTokenSequences, pe.tokenImage, currentTemplate.getName());
278		}
279        catch (TokenMgrError tme)
280        {
281            throw new ParseException("Lexical error: " + tme.toString());
282        }
283        catch (Exception e)
284        {
285            String msg = template.getName() + ": " + e.getMessage();
286            log.error(msg, e);
287            throw new VelocityException(msg, e, getRuntimeServices().getLogContext().getStackTrace());
288        }
289
290        currentTemplate = null;
291
292        return sn;
293    }
294
295    /**
296     *  This method gets a Directive from the directives Hashtable
297     */
298    @Override
299    public Directive getDirective(String directive)
300    {
301        return (Directive) rsvc.getDirective(directive);
302    }
303
304    /**
305     *  This method finds out of the directive exists in the directives Map.
306     */
307    @Override
308    public boolean isDirective(String directive)
309    {
310        return rsvc.getDirective(directive) != null;
311    }
312
313
314    /**
315     * Produces a processed output for an escaped control or
316     * pluggable directive
317     */
318    private String escapedDirective( String strImage )
319    {
320        int iLast = strImage.lastIndexOf("\\");
321
322        String strDirective = strImage.substring(iLast + 1);
323
324        boolean bRecognizedDirective = false;
325
326        // we don't have to call substring method all the time in this method
327        String dirTag = strDirective.substring(1);
328        if (dirTag.charAt(0) == '{')
329        {
330            dirTag = dirTag.substring(1, dirTag.length() - 1);
331        }
332
333        /*
334         *  If this is a predefined derective or if we detect
335         *  a macro definition (this is aproximate at best) then
336         *  we absorb the forward slash.  If in strict reference
337         *  mode then we always absord the forward slash regardless
338         *  if the derective is defined or not.
339         */
340
341        if (strictEscape
342             || isDirective(dirTag)
343             || macroNames.containsKey(dirTag)
344             || rsvc.isVelocimacro(dirTag, currentTemplate))
345        {
346            bRecognizedDirective = true;
347        }
348        else
349        {
350            /* order for speed? */
351
352            if ( dirTag.equals("if")
353                || dirTag.equals("end")
354                || dirTag.equals("set")
355                || dirTag.equals("else")
356                || dirTag.equals("elseif")
357            )
358            {
359                bRecognizedDirective = true;
360            }
361        }
362
363        /*
364         *  if so, make the proper prefix string (let the escapes do their thing..)
365         *  otherwise, just return what it is..
366         */
367
368        if (bRecognizedDirective)
369            return ( strImage.substring(0,iLast/2) + strDirective);
370        else
371            return ( strImage );
372    }
373
374    /**
375     * Check whether there is a left parenthesis with leading optional
376     * whitespaces. This method is used in the semantic look ahead of
377     * Directive method. This is done in code instead of as a production
378     * for simplicity and efficiency.
379     */
380    private boolean isLeftParenthesis()
381    {
382        char c;
383        int no = 0;
384        try {
385            while(true)
386            {
387                /**
388                 * Read a character
389                 */
390                c = velcharstream.readChar();
391                no++;
392                if (c == '(')
393                {
394                    return true;
395                }
396                /**
397                 * if not a white space return
398                 */
399                else if (c != ' ' && c != '\n' && c != '\r' && c != '\t')
400                {
401                    return false;
402                }
403            }
404        }
405        catch(IOException e)
406        {
407        }
408        finally
409        {
410            /**
411             * Backup the stream to the initial state
412             */
413            velcharstream.backup(no);
414        }
415        return false;
416    }
417
418    /**
419     * Check whether there is a right parenthesis with leading optional
420     * whitespaces. This method is used in the semantic look ahead of
421     * Directive method. This is done in code instead of as a production
422     * for simplicity and efficiency.
423     */
424    private boolean isRightParenthesis()
425    {
426        char c;
427        int no = -1;
428        try {
429            while(true)
430            {
431                /**
432                 * Read a character
433                 */
434                 if (no == -1)
435                 {
436                     switch (getToken(1).kind)
437                     {
438                        case RPAREN:
439                            return true;
440                        case WHITESPACE:
441                        case NEWLINE:
442                            no = 0;
443                            break;
444                        default:
445                            return false;
446                     }
447                 }
448                c = velcharstream.readChar();
449                no++;
450                if (c == ')')
451                {
452                    return true;
453                }
454                /**
455                 * if not a white space return
456                 */
457                else if (c != ' ' && c != '\n' && c != '\r' && c != '\t')
458                {
459                    return false;
460                }
461            }
462        }
463        catch(IOException e)
464        {
465        }
466        finally
467        {
468            /**
469             * Backup the stream to the initial state
470             */
471            if (no > 0) velcharstream.backup(no);
472        }
473        return false;
474    }
475
476    /**
477     * We use this method in a lookahead to determine if we are in a macro
478     * default value assignment.  The standard lookahead is not smart enough.
479     * here we look for the equals after the reference.
480     */
481    private boolean isAssignment()
482    {
483       // Basically if the last character read was not '$' then false
484       if (token_source.getCurrentLexicalState() != REFERENCE) return false;
485
486       char c = ' ';
487       int backup = 0;
488       try
489       {
490           // Read through any white space
491           while(Character.isWhitespace(c))
492           {
493                c = velcharstream.readChar();
494                backup++;
495           }
496
497           // This is what we are ultimately looking for
498           if (c != '=') return false;
499       }
500       catch (IOException e)
501       {
502       }
503       finally
504       {
505           velcharstream.backup(backup);
506       }
507
508       return true;
509    }
510
511    @Override
512    public Template getCurrentTemplate()
513    {
514        return currentTemplate;
515    }
516
517    @Override
518    public void resetCurrentTemplate()
519    {
520        currentTemplate = null;
521    }
522
523    @Override
524    public char dollar()
525    {
526        return dollar;
527    }
528
529    @Override
530    public char hash()
531    {
532        return hash;
533    }
534
535    @Override
536    public char at()
537    {
538        return at;
539    }
540
541    @Override
542    public char asterisk()
543    {
544        return asterisk;
545    }
546
547    private char dollar = '$';
548    private char hash = '#';
549    private char at = '@';
550    private char asterisk = '*';
551}
552
553PARSER_END(${parser.basename}Parser)
554
555TOKEN_MGR_DECLS:
556{
557    private int fileDepth = 0;
558
559    private int lparen = 0;
560    private int rparen = 0;
561    private int curlyLevel = 0;
562    List stateStack = new ArrayList(50);
563
564    private boolean inComment;
565    private boolean inSet;
566
567    /**
568     * Our own trace method. Use sparsingly in production, since each
569     * and every call will introduce an execution branch and slow down parsing.
570     */
571    public static void trace(String message)
572    {
573        ${parser.basename}Parser.trace(message);
574    }
575
576    /**
577     * Switches to a new state (add some log to the default method)
578     */
579     public void switchTo(int lexState)
580     {
581        trace(" switch to " + lexStateNames[lexState]);
582        SwitchTo(lexState);
583     }
584
585    public int getCurrentLexicalState()
586    {
587        return curLexState;
588    }
589
590    /**
591     *  pops a state off the stack, and restores paren counts
592     *
593     *  @return boolean : success of operation
594     */
595    public boolean stateStackPop()
596    {
597        ParserState s;
598        try
599        {
600            s = (ParserState) stateStack.remove(stateStack.size() - 1); // stack.pop
601        }
602        catch(IndexOutOfBoundsException e)
603        {
604            // empty stack
605            lparen=0;
606            switchTo(DEFAULT);
607            return false;
608        }
609
610        trace(" stack pop (" + stateStack.size() + ")");
611        lparen = s.lparen;
612        rparen = s.rparen;
613        curlyLevel = s.curlyLevel;
614
615        switchTo(s.lexstate);
616
617        return true;
618    }
619
620    /**
621     *  pushes the current state onto the 'state stack',
622     *  and maintains the parens counts
623     *  public because we need it in PD &amp; VM handling
624     *
625     *  @return boolean : success.  It can fail if the state machine
626     *     gets messed up (do don't mess it up :)
627     */
628    public boolean stateStackPush()
629    {
630        trace(" (" + stateStack.size() + ") pushing cur state : " + lexStateNames[curLexState] );
631
632        ParserState s = new ParserState();
633        s.lparen = lparen;
634        s.rparen = rparen;
635        s.curlyLevel = curlyLevel;
636        s.lexstate = curLexState;
637
638        stateStack.add(s); // stack.push
639
640        lparen = 0;
641        curlyLevel = 0;
642
643        return true;
644    }
645
646    /**
647     *  Clears all state variables, resets to
648     *  start values, clears stateStack.  Call
649     *  before parsing.
650     */
651    public void clearStateVars()
652    {
653        stateStack.clear();
654
655        lparen = 0;
656        rparen = 0;
657        curlyLevel = 0;
658        inComment = false;
659        inSet = false;
660
661        return;
662    }
663
664    public void setInSet(boolean value)
665    {
666        inSet = value;
667    }
668
669    public boolean isInSet()
670    {
671        return inSet;
672    }
673
674    /**
675     * Holds the state of the parsing process.
676     */
677    private static class ParserState
678    {
679        int lparen;
680        int rparen;
681        int curlyLevel;
682        int lexstate;
683    }
684
685    /**
686     *  handles the dropdown logic when encountering a RPAREN
687     */
688    private void RPARENHandler()
689    {
690        /*
691         *  Ultimately, we want to drop down to the state below
692         *  the one that has an open (if we hit bottom (DEFAULT),
693         *  that's fine. It's just text schmoo.
694         */
695
696        boolean closed = false;
697
698        if (inComment)
699            closed = true;
700
701        while( !closed )
702        {
703            /*
704             * look at current state.  If we haven't seen a lparen
705             * in this state then we drop a state, because this
706             * lparen clearly closes our state
707             */
708
709            if( lparen > 0)
710            {
711                /*
712                 *  if rparen + 1 == lparen, then this state is closed.
713                 * Otherwise, increment and keep parsing
714                 */
715
716                 if( lparen == rparen + 1)
717                 {
718                       stateStackPop();
719                 }
720                else
721                {
722                    rparen++;
723                }
724
725                 closed = true;
726            }
727            else
728            {
729                /*
730                 * now, drop a state
731                 */
732
733                if(!stateStackPop())
734                    break;
735            }
736        }
737    }
738}
739
740/* ------------------------------------------------------------------------
741 *
742 * Tokens
743 *
744 * ------------------------------------------------------------------------- */
745
746/* The VelocityCharStream will send a zero-width whitespace
747   just before EOF to let us accept a terminal $ or #
748*/
749<PRE_DIRECTIVE,PRE_REFERENCE,PRE_OLD_REFERENCE>
750TOKEN :
751{
752    <LONE_SYMBOL: "\u001C" >
753    {
754        stateStackPop();
755    }
756}
757
758/* In all other states, keep the zero-width whitespace for now */
759<REFERENCE,REFMODIFIER,OLD_REFMODIFIER,REFMOD3,REFINDEX,DIRECTIVE,REFMOD2,DEFAULT,REFMOD,IN_TEXTBLOCK,IN_MULTILINE_COMMENT,IN_FORMAL_COMMENT,IN_SINGLE_LINE_COMMENT>
760TOKEN :
761{
762    <ZERO_WIDTH_WHITESPACE: "\u001C">
763}
764
765<REFERENCE, REFMODIFIER, OLD_REFMODIFIER, REFMOD3>
766TOKEN:
767{
768   <INDEX_LBRACKET: "[">
769   {
770     stateStackPush();
771     switchTo(REFINDEX);
772   }
773   |
774   /* we need to give precedence to the logical 'or' here, it's a hack to avoid multiplying parsing modes */
775   <LOGICAL_OR_2: "||">
776   {
777       stateStackPop();
778   }
779   |
780   <PIPE: "|">
781   {
782       if (curlyLevel == 1)
783       {
784           switchTo(ALT_VAL);
785       }
786       else
787       {
788           stateStackPop();
789       }
790   }
791}
792
793<REFINDEX>
794TOKEN:
795{
796   <INDEX_RBRACKET: "]">
797   {
798     stateStackPop();
799   }
800}
801
802
803<DIRECTIVE,REFMOD2,ALT_VAL>
804TOKEN:
805{
806    <LBRACKET: "[">
807|   <RBRACKET: "]">
808|   <COMMA:",">
809}
810
811<DIRECTIVE,REFMOD2,ALT_VAL>
812TOKEN:
813{
814  <DOUBLEDOT : ".." >
815}
816
817<DIRECTIVE, REFMOD2,ALT_VAL>
818TOKEN:
819{
820  <COLON : ":" >
821}
822
823<DIRECTIVE, REFMOD2, ALT_VAL>
824TOKEN :
825{
826    <LEFT_CURLEY : "{" >
827    {
828        ++curlyLevel;
829    }
830  |
831    <RIGHT_CURLEY : "}" >
832    {
833        --curlyLevel;
834        if (curLexState == ALT_VAL && curlyLevel == 0)
835        {
836            stateStackPop();
837        }
838    }
839}
840
841<DIRECTIVE,REFMODIFIER,OLD_REFMODIFIER>
842TOKEN:
843{
844    <LPAREN: "(">
845    {
846        if (!inComment)
847            lparen++;
848
849        /*
850         * If in REFERENCE and we have seen the dot, then move
851         * to REFMOD2 -> Modifier()
852         */
853
854        if (curLexState == REFMODIFIER || curLexState == OLD_REFMODIFIER )
855            switchTo( REFMOD2 );
856    }
857}
858
859/*
860 * we never will see a ')' in anything but DIRECTIVE and REFMOD2.
861 * Each have their own
862 */
863<DIRECTIVE>
864TOKEN:
865{
866    <RPAREN: ")">
867    {
868       RPARENHandler();
869    }
870}
871
872
873<REFMOD2>
874TOKEN:
875{
876    /*
877     * in REFMOD2, we don't want to bind the whitespace and \n like we
878     * do when closing a directive.
879     */
880    <REFMOD2_RPAREN: ")">
881    {
882        /*
883         * need to simply switch back to REFERENCE, not drop down the stack
884         * because we can (infinitely) chain, ala
885         * $foo.bar().blargh().woogie().doogie()
886         */
887
888        switchTo( REFMOD3 );
889    }
890}
891
892/*----------------------------------------------
893 *
894 *  escape "\\" handling for the built-in directives
895 *
896 *--------------------------------------------- */
897TOKEN:
898{
899    /*
900     *  We have to do this, because we want these to be a Text node, and
901     *  whatever follows to be peer to this text in the tree.
902     *
903     *  We need to touch the ASTs for these, because we want an even # of \'s
904     *  to render properly in front of the block
905     *
906     *  This is really simplistic.  I actually would prefer to find them in
907     *  grammatical context, but I am neither smart nor rested, a receipe
908     *  for disaster, another long night with Mr. Parser, or both.
909     */
910
911    <ESCAPE_DIRECTIVE :  (<DOUBLE_ESCAPE>)* "\\${parser.char.hash}" (<WORD> | <BRACKETED_WORD>) >
912}
913
914
915/*
916 * We added the lexical states REFERENCE, REFMODIFIER, REFMOD2 to
917 * address JIRA issue VELOCITY-631. With SET_DIRECTIVE only in the
918 * DEFAULT lexical state the following VTL fails "$a#set($b = 1)"
919 * because the Reference token uses LOOKAHEAD(2) combined with the
920 * fact that we explicity set the lex state to REFERENCE with the $
921 * token, which means we would never evaluate this token during the
922 * look ahead.  This general issue is disscussed here:
923 *
924 *  http://www.engr.mun.ca/~theo/JavaCC-FAQ/javacc-faq-ie.htm#tth_sEc3.12
925 *
926 */
927<DEFAULT, PRE_REFERENCE, PRE_OLD_REFERENCE, REFERENCE, REFMODIFIER, OLD_REFMODIFIER, REFMOD2, REFMOD3>
928TOKEN:
929{
930  <SET_DIRECTIVE: ("${parser.char.hash}set" | "${parser.char.hash}{set}")  (" "|"\t")* "(">
931    {
932        if (! inComment)
933        {
934            trace(" #set :  going to DIRECTIVE" );
935
936            stateStackPush();
937            setInSet(true);
938            switchTo(DIRECTIVE);
939        }
940
941        /*
942         *  need the LPAREN action
943         */
944
945        if (!inComment)
946        {
947            lparen++;
948
949            /*
950             * If in REFERENCE and we have seen the dot, then move
951             * to REFMOD2 -> Modifier()
952             */
953
954            if (curLexState == REFMODIFIER || curLexState == OLD_REFMODIFIER )
955                switchTo( REFMOD2 );
956        }
957   }
958}
959
960<*>
961MORE :
962{
963    /*
964     *   Note : DOLLARBANG is a duplicate of DOLLAR.  They must be identical.
965     */
966
967    <DOLLAR: ("\\")* "${parser.char.dollar}">
968    {
969        if (! inComment)
970        {
971            /*
972             * if we find ourselves in REFERENCE or PRE_REFERENCE, we need to pop down
973             * to end the previous ref
974             */
975
976            if (curLexState == REFERENCE || curLexState == PRE_REFERENCE || curLexState == PRE_OLD_REFERENCE)
977            {
978                stateStackPop();
979            }
980
981            int preReferenceState = parser.hyphenAllowedInIdentifiers ? PRE_OLD_REFERENCE : PRE_REFERENCE;
982
983            trace( " $  : going to " + lexStateNames[preReferenceState]);
984
985            /* do not push PRE states */
986            if (curLexState != PRE_REFERENCE && curLexState != PRE_DIRECTIVE && curLexState != PRE_OLD_REFERENCE)
987            {
988                stateStackPush();
989            }
990            switchTo(preReferenceState);
991        }
992    }
993
994|   <DOLLARBANG: ("\\")* "${parser.char.dollar}" ("\\")* "!">
995    {
996        if (! inComment)
997        {
998            /*
999             * if we find ourselves in REFERENCE or PRE_REFERENCE, we need to pop down
1000             * to end the previous ref
1001             */
1002
1003            if (curLexState == REFERENCE || curLexState == PRE_REFERENCE || curLexState == PRE_OLD_REFERENCE)
1004            {
1005                stateStackPop();
1006            }
1007
1008            int preReferenceState = parser.hyphenAllowedInIdentifiers ? PRE_OLD_REFERENCE : PRE_REFERENCE;
1009
1010            trace( " $  : going to " + lexStateNames[preReferenceState]);
1011
1012            /* do not push PRE states */
1013            if (curLexState != PRE_REFERENCE && curLexState != PRE_DIRECTIVE && curLexState != PRE_OLD_REFERENCE)
1014            {
1015                stateStackPush();
1016            }
1017            switchTo(preReferenceState);
1018        }
1019    }
1020
1021|   "${parser.char.hash}[["
1022    {
1023       if (!inComment)
1024       {
1025           inComment = true;
1026            /* do not push PRE states */
1027            if (curLexState != PRE_REFERENCE && curLexState != PRE_DIRECTIVE && curLexState != PRE_OLD_REFERENCE)
1028            {
1029                stateStackPush();
1030            }
1031           switchTo( IN_TEXTBLOCK );
1032       }
1033    }
1034
1035|   <"${parser.char.hash}${parser.char.asterisk}${parser.char.asterisk}" ~["${parser.char.hash}","\u001C"]>
1036    {
1037    	if (!inComment)
1038    	{
1039	    input_stream.backup(1);
1040	    inComment = true;
1041            /* do not push PRE states */
1042            if (curLexState != PRE_REFERENCE && curLexState != PRE_DIRECTIVE && curLexState != PRE_OLD_REFERENCE)
1043            {
1044	        stateStackPush();
1045            }
1046	    switchTo( IN_FORMAL_COMMENT);
1047    	}
1048    }
1049
1050|   "${parser.char.hash}${parser.char.asterisk}"
1051    {
1052    	if (!inComment)
1053    	{
1054	    inComment=true;
1055            /* do not push PRE states */
1056            if (curLexState != PRE_REFERENCE && curLexState != PRE_DIRECTIVE && curLexState != PRE_OLD_REFERENCE)
1057            {
1058	        stateStackPush();
1059            }
1060	    switchTo( IN_MULTI_LINE_COMMENT );
1061    	}
1062    }
1063
1064|   <HASH : "${parser.char.hash}" >
1065    {
1066        if (! inComment)
1067        {
1068            /*
1069             * We can have the situation where #if($foo)$foo#end.
1070             * We need to transition out of REFERENCE before going to DIRECTIVE.
1071             * I don't really like this, but I can't think of a legal way
1072             * you are going into DIRECTIVE while in REFERENCE.  -gmj
1073             */
1074
1075            if (curLexState == REFERENCE || curLexState == PRE_REFERENCE || curLexState == PRE_OLD_REFERENCE || curLexState == REFMODIFIER || curLexState == OLD_REFMODIFIER )
1076            {
1077                stateStackPop();
1078            }
1079
1080            trace(" # :  going to PRE_DIRECTIVE" );
1081
1082            /* do not push PRE states */
1083            if (curLexState != PRE_REFERENCE && curLexState != PRE_DIRECTIVE && curLexState != PRE_OLD_REFERENCE)
1084            {
1085                stateStackPush();
1086            }
1087            switchTo(PRE_DIRECTIVE);
1088        }
1089  }
1090}
1091
1092// treat the single line comment case separately
1093// to avoid ##<EOF> errors
1094<DEFAULT,PRE_DIRECTIVE,DIRECTIVE,REFERENCE,PRE_REFERENCE,PRE_OLD_REFERENCE,REFMOD2,REFMOD3,REFMODIFIER,OLD_REFMODIFIER>
1095TOKEN :
1096{
1097   <SINGLE_LINE_COMMENT_START: "${parser.char.hash}${parser.char.hash}">
1098   {
1099        if (!inComment)
1100        {
1101            if (curLexState == REFERENCE || curLexState == PRE_REFERENCE || curLexState == PRE_OLD_REFERENCE)
1102            {
1103                stateStackPop();
1104            }
1105
1106            inComment = true;
1107            stateStackPush();
1108            switchTo(IN_SINGLE_LINE_COMMENT);
1109        }
1110   }
1111}
1112
1113/* -----------------------------------------------------------------------
1114 *
1115 *   *_COMMENT Lexical tokens
1116 *
1117 *-----------------------------------------------------------------------*/
1118<IN_SINGLE_LINE_COMMENT>
1119TOKEN :
1120{
1121  <SINGLE_LINE_COMMENT: "\n" | "\r" | "\r\n">
1122  {
1123     inComment = false;
1124     stateStackPop();
1125     if (curLexState == REFERENCE || curLexState == REFMOD3)
1126     {
1127       // end of reference: pop again
1128       stateStackPop();
1129     }
1130  }
1131
1132}
1133
1134<IN_FORMAL_COMMENT>
1135TOKEN :
1136{
1137  <FORMAL_COMMENT: "${parser.char.asterisk}${parser.char.hash}" >
1138  {
1139    inComment = false;
1140    stateStackPop();
1141     if (curLexState == REFERENCE || curLexState == REFMOD3)
1142     {
1143       // end of reference: pop again
1144       stateStackPop();
1145     }
1146  }
1147}
1148
1149<IN_MULTI_LINE_COMMENT>
1150TOKEN :
1151{
1152  <MULTI_LINE_COMMENT: "${parser.char.asterisk}${parser.char.hash}" >
1153  {
1154    inComment = false;
1155    stateStackPop();
1156     if (curLexState == REFERENCE || curLexState == REFMOD3)
1157     {
1158       // end of reference: pop again
1159       stateStackPop();
1160     }
1161  }
1162}
1163
1164<IN_TEXTBLOCK>
1165TOKEN :
1166{
1167  <TEXTBLOCK: "]]${parser.char.hash}" >
1168  {
1169    inComment = false;
1170    stateStackPop();
1171  }
1172}
1173
1174<IN_SINGLE_LINE_COMMENT,IN_FORMAL_COMMENT,IN_MULTI_LINE_COMMENT>
1175SKIP :
1176{
1177  < ~[] >
1178}
1179
1180<IN_TEXTBLOCK>
1181MORE :
1182{
1183  < ~["\u001C"] >
1184}
1185
1186/* -----------------------------------------------------------------------
1187 *
1188 *  DIRECTIVE Lexical State (some of it, anyway)
1189 *
1190 * ---------------------------------------------------------------------- */
1191
1192<DEFAULT,REFINDEX,REFMOD2,DIRECTIVE,ALT_VAL>
1193TOKEN:
1194{
1195    <WHITESPACE : ([" ","\t"])+>
1196|   <NEWLINE : ("\n" | "\r" | "\r\n") >
1197    {
1198        trace(" NEWLINE :");
1199
1200        /* if (isInSet()) */
1201        setInSet(false);
1202    }
1203}
1204
1205/* needed for stuff like #foo() followed by ( '$' | '#' )* followed by ( <WHITESPACE> | <ENDLINE> )
1206   so that directive postfix doesn't eat the '$'s and '#'s
1207*/
1208<PRE_DIRECTIVE, PRE_REFERENCE, PRE_OLD_REFERENCE>
1209TOKEN:
1210{
1211  <SUFFIX: ([" ","\t"])* ("\n" | "\r" | "\r\n")>
1212  {
1213    stateStackPop();
1214    }
1215}
1216
1217<DIRECTIVE,REFMOD2,REFINDEX,ALT_VAL>
1218TOKEN :
1219{
1220//   <STRING_LITERAL: ( "\"" ( ~["\"","\n","\r"] )* "\"" ) | ( "'" ( ~["'","\n","\r"] )* "'" ) >
1221  < STRING_LITERAL:
1222      ("\""
1223        (   (~["\"","\u001C"])
1224          | ("\\"
1225              ( ["n","t","b","r","f"]
1226              | ["0"-"7"] ( ["0"-"7"] )?
1227              | ["0"-"3"] ["0"-"7"] ["0"-"7"]
1228              | "u" ["0"-"9", "a"-"f", "A"-"F"] ["0"-"9", "a"-"f", "A"-"F"] ["0"-"9", "a"-"f", "A"-"F"] ["0"-"9", "a"-"f", "A"-"F"]
1229              )
1230            )
1231          | ("\"\"")
1232          | ( "\\" (" ")* "\n")
1233        )*
1234        "\""
1235      )
1236    |
1237    ("\'"
1238        (   (~["\'","\u001C"])
1239          | ("''")
1240          | ( "\\" (" ")* "\n")
1241        )*
1242        "\'"
1243      )
1244  >
1245
1246    {
1247        /*
1248         *  - if we are in DIRECTIVE and haven't seen ( yet, then also drop out.
1249         *      don't forget to account for the beloved yet wierd #set
1250         *  - finally, if we are in REFMOD2 (remember : $foo.bar( ) then " is ok!
1251         */
1252
1253         if( curLexState == DIRECTIVE && !isInSet() && lparen == 0)
1254            stateStackPop();
1255    }
1256}
1257
1258<REFERENCE,DIRECTIVE,REFMODIFIER,OLD_REFMODIFIER,REFMOD2,REFINDEX,ALT_VAL>
1259TOKEN:
1260{
1261   <TRUE: "true">
1262|   <FALSE: "false">
1263}
1264
1265<DIRECTIVE,REFMOD2,REFINDEX,ALT_VAL>
1266TOKEN :
1267{
1268    <MINUS: "-">
1269|   <PLUS: "+">
1270|   <MULTIPLY: "*">
1271|   <DIVIDE: "/">
1272|   <MODULUS: "%">
1273|   <LOGICAL_AND: "&&" | "and" >
1274|   <LOGICAL_OR: "||" | "or" >
1275|   <LOGICAL_LT: "<" | "lt" >
1276|   <LOGICAL_LE: "<=" | "le" >
1277|   <LOGICAL_GT: ">" | "gt" >
1278|   <LOGICAL_GE: ">=" | "ge" >
1279|   <LOGICAL_EQUALS: "==" | "eq" >
1280|   <LOGICAL_NOT_EQUALS: "!=" | "ne" >
1281|   <LOGICAL_NOT: "!" | "not" >
1282|   <EQUALS: "=" >
1283}
1284
1285<PRE_DIRECTIVE>
1286TOKEN :
1287{
1288    <END: ( "end" | "{end}" )>
1289    {
1290        stateStackPop();
1291    }
1292
1293|   <IF_DIRECTIVE: "if" | "{if}">
1294    {
1295        switchTo(DIRECTIVE);
1296    }
1297
1298|   <ELSEIF: "elseif" | "{elseif}">
1299    {
1300        switchTo(DIRECTIVE);
1301    }
1302
1303|   <ELSE: "else" | "{else}">
1304    {
1305        stateStackPop();
1306    }
1307}
1308
1309<PRE_DIRECTIVE,DIRECTIVE,REFMOD2,REFINDEX,ALT_VAL>
1310TOKEN:
1311{
1312   <#DIGIT: [ "0"-"9" ] >
1313
1314    /*
1315     * treat FLOATING_POINT_LITERAL and INTEGER_LITERAL differently as a range can only handle integers.
1316     */
1317
1318    /**
1319     * Note -- we also define an integer as ending with a double period,
1320     * in order to avoid 1..3 being defined as floating point (1.) then a period, then a integer
1321    */
1322|   <INTEGER_LITERAL: ("-")? (<DIGIT>)+ ("..")? >
1323    {
1324
1325        /*
1326         * Remove the double period if it is there
1327         */
1328        if (matchedToken.image.endsWith("..")) {
1329            input_stream.backup(2);
1330            matchedToken.image = matchedToken.image.substring(0,matchedToken.image.length()-2);
1331        }
1332
1333        /*
1334         * check to see if we are in set
1335         *    ex.  #set($foo = $foo + 3)
1336         *  because we want to handle the \n after
1337         */
1338
1339        if ( lparen == 0 && !isInSet() && curLexState != REFMOD2 && curLexState != REFINDEX && curLexState != ALT_VAL)
1340        {
1341            stateStackPop();
1342        }
1343    }
1344
1345|    <FLOATING_POINT_LITERAL:
1346        ("-")? (<DIGIT>)+ "." (<DIGIT>)* (<EXPONENT>)?
1347      | ("-")? "." (<DIGIT>)+ (<EXPONENT>)?
1348      | ("-")? (<DIGIT>)+ <EXPONENT>
1349    >
1350    {
1351        /*
1352         * check to see if we are in set
1353         *    ex.  #set $foo = $foo + 3
1354         *  because we want to handle the \n after
1355         */
1356
1357        if ( lparen == 0 && !isInSet() && curLexState != REFMOD2 && curLexState != ALT_VAL)
1358        {
1359            stateStackPop();
1360    }
1361}
1362|
1363    <#EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ >
1364
1365}
1366
1367/**
1368 *  TODO, the "@" symbol for block macros to be correct really should prefix WORD
1369 *  and BRACKETED_WORD, e.g., <WORD ["@"] ( <LETTER... etc...
1370 *  However, having the conditional character at the beginning screws up
1371 *  Macro parse.  As it is now you can have #@1234 defined as a macro
1372 *  Which is not correct.
1373 */
1374
1375<PRE_DIRECTIVE,DIRECTIVE>
1376TOKEN:
1377{
1378    <#LETTER: [ "a"-"z", "A"-"Z" ] >
1379|   <#DIRECTIVE_CHAR: [ "a"-"z", "A"-"Z", "0"-"9", "_" ] >
1380|   <WORD: ( <LETTER> | ["_"] | ["${parser.char.at}"]) (<DIRECTIVE_CHAR>)* >
1381|   <BRACKETED_WORD: "{" ( <LETTER> | ["_"] | ["${parser.char.at}"]) (<DIRECTIVE_CHAR>)* "}" >
1382}
1383
1384/* -----------------------------------------------------------------------
1385 *
1386 *  REFERENCE Lexical States
1387 *
1388 *  This is more than a single state, because of the  structure of
1389 *  the VTL references.  We use three states because the set of tokens
1390 *  for each state can be different.
1391 *
1392 *  $foo.bar( "arg" )
1393 *  ^ ^ ^   ^       ^
1394 *  | | |   |       |
1395 *  |_________________ >  PRE_REFERENCE : state initiated by the '$' character.
1396 *    | |   |       |                     (or PRE_OLD_REFERENCE if '-' is allowed in identifiers)
1397 *    |________________>  REFERENCE : state initiated by the identifier. Continues
1398 *      |   |       |     until end of the reference, or the . character.
1399 *      |_____________ >  REFMODIFIER : state switched to when the <DOT> is encountered.
1400 *          |       |                     (or OLD_REFMODIFIER if '-' is allowed in identifiers)
1401 *          |       |     note that this is a switch, not a push. See notes at bottom.
1402 *          |_________ >  REFMOD2 : state switch to when the LPAREN is encountered.
1403 *                  |     again, this is a switch, not a push.
1404 *                  |_ >  REFMOD3 : state only checking for a possible '.' or '['  continuation.
1405 *
1406 *  During the REFERENCE, REFMODIFIER or REFMOD3 lex states we will switch to:
1407 *  - REFINDEX if a bracket '[' is encountered:  $foo[1], $foo.bar[1], $foo.bar( "arg" )[1]
1408 *  - ALT_VAL if a pipe '|' is encountered (only for formal references): ${foo|'foo'}
1409 * ---------------------------------------------------------------------------- */
1410
1411<PRE_REFERENCE,REFMODIFIER,REFMOD2>
1412TOKEN :
1413{
1414    <#ALPHA_CHAR: ["a"-"z", "A"-"Z", "_"] >
1415|   <#IDENTIFIER_CHAR: [ "a"-"z", "A"-"Z", "0"-"9", "_" ] >
1416|   <IDENTIFIER:  ( <ALPHA_CHAR> ) (<IDENTIFIER_CHAR>)* >
1417    {
1418        if (curLexState == PRE_REFERENCE)
1419        {
1420            switchTo(REFERENCE);
1421        }
1422    }
1423}
1424
1425<PRE_OLD_REFERENCE,OLD_REFMODIFIER>
1426TOKEN :
1427{
1428    <#OLD_ALPHA_CHAR: ["a"-"z", "A"-"Z", "_"] >
1429|   <#OLD_IDENTIFIER_CHAR: [ "a"-"z", "A"-"Z", "0"-"9", "_", "-" ] >
1430|   <OLD_IDENTIFIER:  ( <OLD_ALPHA_CHAR> ) (<OLD_IDENTIFIER_CHAR>)* >
1431    {
1432        if (curLexState == PRE_OLD_REFERENCE)
1433        {
1434            switchTo(REFERENCE);
1435        }
1436    }
1437}
1438
1439
1440<REFERENCE,REFMODIFIER,OLD_REFMODIFIER,REFMOD2,REFMOD3>
1441TOKEN:
1442{
1443   <DOT: "." <ALPHA_CHAR>>
1444    {
1445        /*
1446         * push the alpha char back into the stream so the following identifier
1447         * is complete
1448         */
1449
1450        input_stream.backup(1);
1451
1452        /*
1453         * and munge the <DOT> so we just get a . when we have normal text that
1454         * looks like a ref.ident
1455         */
1456
1457        matchedToken.image = ".";
1458
1459        int refModifierState = parser.hyphenAllowedInIdentifiers ? OLD_REFMODIFIER : REFMODIFIER;
1460
1461        trace("DOT : switching to " + lexStateNames[refModifierState]);
1462        switchTo(refModifierState);
1463
1464    }
1465}
1466
1467<PRE_REFERENCE,PRE_OLD_REFERENCE,REFERENCE,REFMODIFIER,OLD_REFMODIFIER,REFMOD3>
1468TOKEN :
1469{
1470    <LCURLY: "{">
1471    {
1472        ++curlyLevel;
1473    }
1474|   <RCURLY: "}">
1475    {
1476        /* maybe it wasn't for our state */
1477        while (curlyLevel == 0 && curLexState != DEFAULT)
1478        {
1479            stateStackPop();
1480        }
1481        /* At this point, here are all the possible states:
1482         *   - DEFAULT, which means the '}' is schmoo
1483         *   - DIRECTIVE or REFMOD2, which means the '}' is a closing map curly
1484         *   - one of the other REFERENCE states or ALT_VAL, which means the '}' ends the reference
1485         * If we're in the last case, pop up state.
1486         */
1487        if (curLexState != DEFAULT && curLexState != DIRECTIVE && curLexState != REFMOD2)
1488        {
1489            stateStackPop();
1490        }
1491    }
1492}
1493
1494<PRE_REFERENCE,PRE_OLD_REFERENCE,REFERENCE,REFMODIFIER,OLD_REFMODIFIER,REFMOD,REFMOD3>
1495SPECIAL_TOKEN :
1496{
1497    <REFERENCE_TERMINATOR: ~[] >
1498    {
1499        /*
1500         * push every terminator character back into the stream
1501         */
1502
1503        input_stream.backup(1);
1504
1505        trace("REF_TERM :");
1506
1507        stateStackPop();
1508    }
1509}
1510
1511<PRE_DIRECTIVE>
1512SPECIAL_TOKEN :
1513{
1514    <DIRECTIVE_TERMINATOR: ~[] >
1515    {
1516        trace("DIRECTIVE_TERM :");
1517
1518        input_stream.backup(1);
1519        stateStackPop();
1520    }
1521}
1522
1523/* TEXT must end with a newline, and contain at least one non-whitespace character in the first line,
1524   so that the <WHITESPACE> <NEWLINE> sequence is not read as a TEXT (needed for space gobbling)
1525*/
1526TOKEN :
1527{
1528    <DOUBLE_ESCAPE : "\\\\">
1529|   <ESCAPE: "\\" >
1530|   <TEXT: (~["${parser.char.dollar}", "${parser.char.hash}", "\\", "\r", "\n","\u001C"])* (~["${parser.char.dollar}", "${parser.char.hash}", "\\", "\r", "\n", " ", "\t","\u001C"])+ (~["${parser.char.dollar}", "${parser.char.hash}", "\\", "\r", "\n","\u001C"])* <NEWLINE> ((~["${parser.char.dollar}", "${parser.char.hash}", "\\", "\r", "\n","\u001C"])* <NEWLINE>)* >
1531}
1532
1533TOKEN :
1534{
1535    <INLINE_TEXT: (~["${parser.char.dollar}", "${parser.char.hash}", "\\", "\r", "\n","\u001C"])+ >
1536}
1537
1538/**
1539 * This method is what starts the whole parsing
1540 * process. After the parsing is complete and
1541 * the template has been turned into an AST,
1542 * this method returns the root of AST which
1543 * can subsequently be traversed by a visitor
1544 * which implements the ParserVisitor interface
1545 * which is generated automatically by JavaCC
1546 */
1547SimpleNode process() :
1548{
1549    boolean afterNewline = true;
1550}
1551{
1552   ( LOOKAHEAD({ getToken(1).kind != EOF }) afterNewline = Statement(afterNewline) )* <EOF>
1553   { return jjtThis; }
1554}
1555
1556/**
1557 * These are the types of statements that
1558 * are acceptable in Velocity templates.
1559 */
1560boolean Statement(boolean afterNewline) #void :
1561{
1562}
1563{
1564    LOOKAHEAD( { getToken(1).kind == IF_DIRECTIVE || afterNewline && getToken(1).kind == WHITESPACE && getToken(2).kind == IF_DIRECTIVE } ) afterNewline = IfStatement(afterNewline) { return afterNewline; }
1565|   LOOKAHEAD(2) Reference() { return false; }
1566|   LOOKAHEAD(2) afterNewline = Comment() { return afterNewline; }
1567|   Textblock() { return false; }
1568|   LOOKAHEAD( { getToken(1).kind == SET_DIRECTIVE || afterNewline && getToken(1).kind == WHITESPACE && getToken(2).kind == SET_DIRECTIVE } ) afterNewline = SetDirective(afterNewline) { return afterNewline; }
1569|   EscapedDirective() { return false; }
1570|   Escape() { return false; }
1571|   LOOKAHEAD( { getToken(1).kind == WORD || getToken(1).kind == BRACKETED_WORD || afterNewline && getToken(1).kind == WHITESPACE && ( getToken(2).kind == WORD || getToken(2).kind == BRACKETED_WORD ) } ) afterNewline = Directive(afterNewline) { return afterNewline; }
1572|   afterNewline = Text() { return afterNewline; }
1573|   (<NEWLINE>) #Text { return true; }
1574|   (((<INLINE_TEXT>) { afterNewline = false; } ) ((<TEXT>) { afterNewline = true; })? ) #Text { return afterNewline; }
1575|   (<WHITESPACE>) #Text { return false; }
1576|   (<SUFFIX>) #Text { return true; }
1577|   LOOKAHEAD(2) EndingZeroWidthWhitespace() { return afterNewline; }
1578|   (<LOGICAL_OR_2>) #Text { return afterNewline; } // needed here since it can be triggered in <REFERENCE> mode out of any boolean evaluation
1579|   (<ZERO_WIDTH_WHITESPACE>) #Text { afterNewline = !afterNewline; return false; }
1580}
1581
1582void EndingZeroWidthWhitespace() #void : {}
1583{
1584    <ZERO_WIDTH_WHITESPACE> <EOF> {  }
1585}
1586
1587/**
1588 *  used to separate the notion of a valid directive that has been
1589 *  escaped, versus something that looks like a directive and
1590 *  is just schmoo.  This is important to do as a separate production
1591 *  that creates a node, because we want this, in either case, to stop
1592 *  the further parsing of the Directive() tree.
1593 */
1594void EscapedDirective() : {}
1595{
1596    {
1597        Token t = null;
1598    }
1599
1600    t = <ESCAPE_DIRECTIVE>
1601    {
1602        /*
1603         *  churn and burn..
1604         */
1605        t.image = escapedDirective( t.image );
1606    }
1607}
1608
1609/**
1610 *  Used to catch and process escape sequences in grammatical constructs
1611 *  as escapes outside of VTL are just characters.  Right now we have both
1612 *  this and the EscapeDirective() construction because in the EscapeDirective()
1613 *  case, we want to suck in the #&lt;directive&gt; and here we don't.  We just want
1614 *  the escapes to render correctly
1615 */
1616void Escape() : {}
1617{
1618    {
1619        Token t = null;
1620        int count = 0;
1621        boolean control = false;
1622    }
1623
1624   ( LOOKAHEAD(2)  t = <DOUBLE_ESCAPE>
1625    {
1626        count++;
1627    }
1628   )+
1629    {
1630        /*
1631         * first, check to see if we have a control directive
1632         */
1633        switch(t.next.kind ) {
1634            case IF_DIRECTIVE :
1635            case ELSE :
1636            case ELSEIF :
1637            case END :
1638                control = true;
1639                break;
1640        }
1641
1642        /*
1643         * if that failed, lets lookahead to see if we matched a PD or a VM
1644         */
1645        String nTag = t.next.image.substring(1);
1646        if (strictEscape
1647            || isDirective(nTag)
1648            || macroNames.containsKey(nTag)
1649            || rsvc.isVelocimacro(nTag, currentTemplate))
1650        {
1651            control = true;
1652        }
1653
1654        jjtThis.val = "";
1655
1656        for( int i = 0; i < count; i++)
1657            jjtThis.val += ( control ? "\\" : "\\\\");
1658    }
1659
1660}
1661
1662boolean Comment() : {}
1663{
1664	<SINGLE_LINE_COMMENT_START> ( <SINGLE_LINE_COMMENT> ) ? { return true; }
1665|   <MULTI_LINE_COMMENT> { return false; }
1666|   <FORMAL_COMMENT> { return false; }
1667}
1668
1669void Textblock() : {}
1670{
1671   <TEXTBLOCK>
1672}
1673
1674void FloatingPointLiteral() : {}
1675{
1676    <FLOATING_POINT_LITERAL>
1677}
1678
1679void IntegerLiteral() : {}
1680{
1681     <INTEGER_LITERAL>
1682}
1683
1684void StringLiteral() : {}
1685{
1686    <STRING_LITERAL>
1687}
1688
1689/**
1690 * This method corresponds to variable
1691 * references in Velocity templates.
1692 * The following are examples of variable
1693 * references that may be found in a
1694 * template:
1695 *
1696 * $foo
1697 * $bar
1698 *
1699 */
1700void Identifier() : {}
1701{
1702    <IDENTIFIER> | <OLD_IDENTIFIER>
1703}
1704
1705void Word() : {}
1706{
1707    <WORD>
1708}
1709
1710/**
1711 *   Supports the arguments for the Pluggable Directives
1712 */
1713int DirectiveArg() #void : {}
1714{
1715    Reference()
1716    {
1717        return ParserTreeConstants.JJTREFERENCE;
1718    }
1719|   Word()
1720    {
1721        return ParserTreeConstants.JJTWORD;
1722    }
1723|   StringLiteral()
1724    {
1725        return ParserTreeConstants.JJTSTRINGLITERAL;
1726    }
1727
1728|   IntegerLiteral()
1729    {
1730        return ParserTreeConstants.JJTINTEGERLITERAL;
1731    }
1732    /*
1733     * Need to put this before the floating point expansion
1734     */
1735|   LOOKAHEAD(  <LBRACKET> (<WHITESPACE> | <NEWLINE>)* ( Reference() | IntegerLiteral())     (<WHITESPACE> | <NEWLINE>)* <DOUBLEDOT> ) IntegerRange()
1736    {
1737        return ParserTreeConstants.JJTINTEGERRANGE;
1738    }
1739|   FloatingPointLiteral()
1740    {
1741        return ParserTreeConstants.JJTFLOATINGPOINTLITERAL;
1742    }
1743|   Map()
1744    {
1745        return ParserTreeConstants.JJTMAP;
1746    }
1747|   ObjectArray()
1748    {
1749        return ParserTreeConstants.JJTOBJECTARRAY;
1750    }
1751|   True()
1752    {
1753        return ParserTreeConstants.JJTTRUE;
1754    }
1755|   False()
1756    {
1757        return ParserTreeConstants.JJTFALSE;
1758    }
1759}
1760
1761void DirectiveAssign() : {}
1762{
1763    Reference()
1764}
1765
1766
1767/**
1768 *   Supports the Pluggable Directives
1769 *     #foo( arg+ )
1770 * @return true if ends with a newline
1771 */
1772boolean Directive(boolean afterNewline) :
1773{
1774    Token id = null, t = null, u = null, end = null, _else = null;
1775    int argType;
1776    int argPos = 0;
1777    Directive d;
1778    int directiveType;
1779    boolean isVM = false;
1780    boolean isMacro = false;
1781    ArrayList argtypes = new ArrayList(4);
1782    String blockPrefix = "";
1783    ASTBlock block = null, elseBlock = null;
1784    boolean hasParentheses = false;
1785    boolean newlineAtStart = afterNewline;
1786}
1787{
1788    [
1789      (t = <WHITESPACE>)
1790      {
1791          // only possible if not after new line
1792          jjtThis.setPrefix(t.image);
1793          t = null;
1794      }
1795    ]
1796    /*
1797     * note that if we were escaped, that is now handled by
1798     * EscapedDirective()
1799     */
1800    ((id = <WORD>) | (id = <BRACKETED_WORD>))
1801    {
1802        String directiveName;
1803        int p = id.image.lastIndexOf(hash);
1804        if (id.kind == StandardParserConstants.BRACKETED_WORD)
1805        {
1806            directiveName = id.image.substring(p + 2, id.image.length() - 1);
1807        }
1808        else
1809        {
1810            directiveName = id.image.substring(p + 1);
1811        }
1812
1813        d = getDirective(directiveName);
1814
1815        /*
1816         *  Velocimacro support : if the directive is macro directive
1817         *   then set the flag so after the block parsing, we add the VM
1818         *   right then. (So available if used w/in the current template )
1819         */
1820
1821        if (directiveName.equals("macro"))
1822        {
1823             isMacro = true;
1824        }
1825
1826        /*
1827         * set the directive name from here.  No reason for the thing to know
1828         * about parser tokens
1829         */
1830
1831        jjtThis.setDirectiveName(directiveName);
1832
1833        if ( d == null)
1834        {
1835            if( directiveName.charAt(0) == at )
1836            {
1837                // block macro call of type: #@foobar($arg1 $arg2) astBody #end
1838                directiveType = Directive.BLOCK;
1839            }
1840            else
1841            {
1842                /*
1843                 *  if null, then not a real directive, but maybe a Velocimacro
1844                 */
1845                isVM = rsvc.isVelocimacro(directiveName, currentTemplate);
1846
1847                directiveType = Directive.LINE;
1848            }
1849        }
1850        else
1851        {
1852            directiveType = d.getType();
1853        }
1854
1855        /*
1856         *  now, switch us out of PRE_DIRECTIVE
1857         */
1858
1859        token_source.switchTo(DIRECTIVE);
1860        argPos = 0;
1861    }
1862
1863
1864     /**
1865      * Look for the pattern [WHITESPACE] <LPAREN>
1866      */
1867    (
1868      LOOKAHEAD( { isLeftParenthesis() } )
1869      /*
1870       *  if this is indeed a token, match the #foo ( arg, arg... ) pattern
1871       */
1872      (
1873        (<WHITESPACE> | <NEWLINE>)* <LPAREN>
1874        (
1875          LOOKAHEAD({ !isRightParenthesis() }) (<WHITESPACE> | <NEWLINE>)* [<COMMA> (<WHITESPACE> | <NEWLINE>)*]
1876          (
1877            [
1878              LOOKAHEAD( { isMacro && isAssignment() })
1879              DirectiveAssign() (<WHITESPACE> | <NEWLINE>)* <EQUALS> ( <WHITESPACE> | <NEWLINE> )*
1880              {
1881                  argtypes.add(ParserTreeConstants.JJTDIRECTIVEASSIGN);
1882              }
1883            ]
1884            LOOKAHEAD( { !isRightParenthesis() } )
1885            (
1886              argType = DirectiveArg()
1887              {
1888                  argtypes.add(argType);
1889                  if (d == null && argType == ParserTreeConstants.JJTWORD)
1890                  {
1891                      if (isVM)
1892                      {
1893                          throw new MacroParseException("Invalid argument "
1894                              + (argPos+1) + " in macro call " + id.image, currentTemplate.getName(), id);
1895                      }
1896                  }
1897                  argPos++;
1898              }
1899            )
1900            |
1901            {
1902                if (!isMacro)
1903                {
1904                    // We only allow line comments in macro definitions for now
1905                    throw new MacroParseException("A Line comment is not allowed in " + id.image
1906                        + " arguments", currentTemplate.getName(), id);
1907                }
1908            }
1909            <SINGLE_LINE_COMMENT_START> [<SINGLE_LINE_COMMENT>]
1910          )
1911        )* (<WHITESPACE> | <NEWLINE>)* <RPAREN>
1912        { hasParentheses = true; }
1913      )
1914      |
1915      {
1916          token_source.stateStackPop();
1917      }
1918    )
1919    { afterNewline = false; }
1920    [
1921      // Conditions where whitespace and newline postfix is eaten by space gobbling at this point:
1922      // - block directive
1923      // - new line before directive without backward compatibility mode
1924      // - backward compatibility mode *with parentheses*
1925      // - #include() or #parse()
1926      LOOKAHEAD(2, { directiveType != Directive.LINE || newlineAtStart && rsvc.getSpaceGobbling() != SpaceGobbling.BC || rsvc.getSpaceGobbling() == SpaceGobbling.BC && hasParentheses || d != null && (d instanceof Include || d instanceof Parse) })
1927        ( [ ( t = <WHITESPACE> ) ] ( u = <NEWLINE> ) )
1928      {
1929          afterNewline = true;
1930          if (directiveType == Directive.LINE)
1931          {
1932              jjtThis.setPostfix(t == null ? u.image : t.image + u.image);
1933          }
1934          else
1935          {
1936              blockPrefix = (t == null ? u.image : t.image + u.image);
1937          }
1938          t = u = null;
1939      }
1940    ]
1941    {
1942        if (d != null)
1943        {
1944            d.checkArgs(argtypes, id, currentTemplate.getName());
1945        }
1946        if (directiveType  == Directive.LINE)
1947        {
1948            return afterNewline;
1949        }
1950    }
1951    /*
1952     *  and the following block if the PD needs it
1953     */
1954    (
1955      (
1956        (
1957          LOOKAHEAD( { getToken(1).kind != END && getToken(1).kind != ELSE && ( !afterNewline || getToken(1).kind != WHITESPACE || getToken(2).kind != END && getToken(2).kind != ELSE ) }) afterNewline = Statement(afterNewline)
1958        )*
1959        {
1960            block = jjtThis;
1961            block.setPrefix(blockPrefix);
1962            blockPrefix = "";
1963        }
1964      )
1965      #Block
1966    )
1967    [
1968      LOOKAHEAD( 1, { afterNewline })
1969      (t = <WHITESPACE>)
1970      {
1971          block.setPostfix(t.image);
1972          t = null;
1973      }
1974    ]
1975    /*
1976     *  then an optional #else for the #foreach directive
1977     */
1978    (
1979      [
1980        LOOKAHEAD( { d != null && (d instanceof Foreach) && getToken(1).kind == ELSE } )
1981        (
1982          (_else = <ELSE>)
1983          (
1984            [
1985              LOOKAHEAD(2) ( [ ( t = <WHITESPACE> ) ] ( u = <NEWLINE> ) )
1986              {
1987                  jjtThis.setPrefix(t == null ? u.image : t.image + u.image);
1988                  t = u = null;
1989                  afterNewline = true;
1990              }
1991            ]
1992            (
1993              LOOKAHEAD( { getToken(1).kind != END && (!afterNewline || getToken(1).kind != WHITESPACE || getToken(2).kind != END) })
1994              afterNewline = Statement(afterNewline)
1995            )*
1996            {
1997                elseBlock = jjtThis;
1998            }
1999          )
2000          #Block
2001          {
2002              int pos = _else.image.lastIndexOf(hash);
2003              if (pos > 0)
2004              {
2005                  block.setMorePostfix(_else.image.substring(0, pos));
2006              }
2007              block = elseBlock;
2008          }
2009        )
2010      ]
2011    )
2012    [
2013      LOOKAHEAD( 1, { afterNewline })
2014      (t = <WHITESPACE>)
2015      {
2016          block.setPostfix(t.image);
2017          t = null;
2018          afterNewline = false;
2019      }
2020    ]
2021    (
2022      (end = <END>)
2023      { afterNewline = false; }
2024      [
2025        LOOKAHEAD(2, { newlineAtStart || rsvc.getSpaceGobbling() == SpaceGobbling.BC })
2026          ( [ ( t = <WHITESPACE> ) ] ( u = <NEWLINE> ) )
2027        {
2028            jjtThis.setPostfix(t == null ? u.image : t.image + u.image);
2029            t = u = null;
2030            afterNewline = true;
2031        }
2032      ]
2033      {
2034          int pos = end.image.lastIndexOf(hash);
2035          if (pos > 0)
2036          {
2037              block.setMorePostfix(end.image.substring(0, pos));
2038          }
2039      }
2040    )
2041    {
2042        /*
2043         *  VM : if we are processing a #macro directive, we need to
2044         *     process the block.  In truth, I can just register the name
2045         *     and do the work later when init-ing.  That would work
2046         *     as long as things were always defined before use.  This way
2047         *     we don't have to worry about forward references and such...
2048         */
2049        if (isMacro)
2050        {
2051            // Add the macro name so that we can peform escape processing
2052            // on defined macros
2053            String macroName = jjtThis.jjtGetChild(0).getFirstToken().image;
2054            macroNames.put(macroName, macroName);
2055        }
2056        if (d != null)
2057        {
2058            d.checkArgs(argtypes, id, currentTemplate.getName());
2059        }
2060        /*
2061         *  VM : end
2062         */
2063        return afterNewline;
2064    }
2065}
2066
2067/**
2068 * for creating a map in a #set
2069 *
2070 *  #set($foo = {$foo : $bar, $blargh : $thingy})
2071 */
2072void Map() : {}
2073{
2074    <LEFT_CURLEY>
2075    (
2076      LOOKAHEAD(( <WHITESPACE> | <NEWLINE> )* Parameter() <COLON>) ( Parameter() <COLON> Parameter() (<COMMA> Parameter() <COLON> Parameter() )* )
2077      |
2078      ( <WHITESPACE> | <NEWLINE> )*
2079     )
2080
2081     /** note: need both tokens as they are generated in different states **/
2082     ( <RIGHT_CURLEY> | <RCURLY> )
2083}
2084
2085void ObjectArray() : {}
2086{
2087    <LBRACKET> [ Parameter() ( <COMMA> Parameter() )* ] <RBRACKET>
2088}
2089
2090
2091/**
2092 *  supports the [n..m] vector generator for use in
2093 *  the #foreach() to generate measured ranges w/o
2094 *  needing explicit support from the app/servlet
2095 */
2096void IntegerRange() : {}
2097{
2098    <LBRACKET> (<WHITESPACE> | <NEWLINE>)*
2099    ( Reference() | IntegerLiteral())
2100    (<WHITESPACE>|<NEWLINE>)* <DOUBLEDOT> (<WHITESPACE>|<NEWLINE>)*
2101    (Reference() | IntegerLiteral())
2102    (<WHITESPACE>|<NEWLINE>)* <RBRACKET>
2103}
2104
2105
2106/**
2107 * A Simplified parameter more suitable for an index position: $foo[$index]
2108 */
2109void IndexParameter() #void: {}
2110{
2111    (<WHITESPACE>|<NEWLINE>)*
2112        (
2113            Expression()
2114        )
2115    (<WHITESPACE>|<NEWLINE>)*
2116}
2117
2118
2119/**
2120 * This method has yet to be fully implemented
2121 * but will allow arbitrarily nested method
2122 * calls
2123 */
2124void Parameter() #void: {}
2125{
2126    (<WHITESPACE>|<NEWLINE>)*
2127    (
2128        StringLiteral()
2129        | IntegerLiteral()
2130        | LOOKAHEAD(  <LBRACKET> ( <WHITESPACE> | <NEWLINE> )*    ( Reference() | IntegerLiteral())     ( <WHITESPACE> | <NEWLINE> )* <DOUBLEDOT> ) IntegerRange()
2131        | Map()
2132        | ObjectArray()
2133        | True()
2134        | False()
2135        | Reference()
2136        | FloatingPointLiteral()
2137        )
2138    (<WHITESPACE>|<NEWLINE>)*
2139}
2140
2141/**
2142 * This method has yet to be fully implemented
2143 * but will allow arbitrarily nested method
2144 * calls
2145 */
2146void Method() : {}
2147{
2148   Identifier() <LPAREN> [ Expression() ( <COMMA> Expression() )* ] <REFMOD2_RPAREN>
2149}
2150
2151
2152void Index() : {}
2153{
2154   <INDEX_LBRACKET> IndexParameter() <INDEX_RBRACKET>
2155}
2156
2157void Reference() : {}
2158{
2159    /*
2160     *  A reference is either $<FOO> or ${<FOO>} or ${<FOO>'|'<ALTERNATE_VALUE>)
2161     */
2162
2163      (
2164       ( <IDENTIFIER> | <OLD_IDENTIFIER> ) (Index())*
2165         (LOOKAHEAD(2) <DOT> (LOOKAHEAD(3) Method() | Identifier() ) (Index())* )*
2166      )
2167      |
2168      (
2169         <LCURLY>
2170         ( <IDENTIFIER> | <OLD_IDENTIFIER> ) (Index())*
2171         (LOOKAHEAD(2) <DOT> (LOOKAHEAD(3) Method() | Identifier() ) (Index())* )*
2172         [ <PIPE> Expression() ]
2173         ( <RCURLY> | <RIGHT_CURLEY> )
2174      )
2175}
2176
2177void True() : {}
2178{
2179    <TRUE>
2180}
2181
2182void False() : {}
2183{
2184    <FALSE>
2185}
2186
2187
2188/**
2189 * This is somewhat of a kludge, the problem is that the parser picks
2190 * up on '$[' , or '$![' as being a Reference, and does not dismiss it even though
2191 * there is no <Identifier> between $ and [,  This has something to do
2192 * with the LOOKAHEAD in Reference, but I never found a way to resolve
2193 * it in a more fashionable way..
2194 */
2195<DEFAULT,PRE_REFERENCE,PRE_OLD_REFERENCE>
2196TOKEN :
2197{
2198  <EMPTY_INDEX : ("$[" | "$![" | "$\\![" | "$.")>
2199}
2200
2201
2202/**
2203 * This method is responsible for allowing
2204 * all non-grammar text to pass through
2205 * unscathed.
2206 * @return true if last read token was a newline
2207 */
2208boolean Text() :
2209{
2210    Token t = null;
2211}
2212{
2213    <TEXT> { return true; }
2214  |  <DOT> { return false; }
2215  |  <RPAREN> { return false; }
2216  |  <LPAREN> { return false; }
2217  |  <INTEGER_LITERAL> { return false; }
2218  |  <FLOATING_POINT_LITERAL> { return false; }
2219  |  <STRING_LITERAL> { return false; }
2220  |  <ESCAPE> { return false; }
2221  |  <LCURLY> { return false; }
2222  |  <RCURLY> { return false; }
2223  |  <EMPTY_INDEX> { return false; }
2224  |  <PIPE> { return false; }
2225  |  t=<LONE_SYMBOL>
2226     {
2227         /* Drop the ending zero-width whitespace */
2228         t.image = t.image.substring(0, t.image.length() - 1); return false;
2229     }
2230}
2231
2232/* -----------------------------------------------------------------------
2233 *
2234 *  Defined Directive Syntax
2235 *
2236 * ----------------------------------------------------------------------*/
2237
2238boolean IfStatement(boolean afterNewline) :
2239{
2240    Token t = null, u = null, end = null;
2241    ASTBlock lastBlock = null;
2242    boolean newlineAtStart = afterNewline;
2243}
2244{
2245    [ ( t = <WHITESPACE> )
2246        {
2247            // only possible if not after new line
2248            jjtThis.setPrefix(t.image);
2249            t = null;
2250        }
2251    ]
2252    <IF_DIRECTIVE> ( <WHITESPACE> | <NEWLINE> )* <LPAREN> Expression() <RPAREN>
2253    (
2254      [
2255        LOOKAHEAD(2) ( [ ( t = <WHITESPACE> ) ] ( u = <NEWLINE> ) )
2256        {
2257            jjtThis.setPrefix(t == null ? u.image : t.image + u.image);
2258            t = u = null;
2259            afterNewline = true;
2260        }
2261      ]
2262      ( LOOKAHEAD(
2263        {
2264            (getToken(1).kind != ELSEIF && getToken(1).kind != ELSE && getToken(1).kind != END) &&
2265              (!afterNewline || getToken(1).kind != WHITESPACE || (getToken(2).kind != ELSEIF && getToken(2).kind != ELSE && getToken(2).kind != END))
2266        })
2267        afterNewline = Statement(afterNewline) )*
2268        {
2269            lastBlock = jjtThis;
2270        }
2271    ) #Block
2272    [ LOOKAHEAD( { getToken(1).kind == ELSEIF || (afterNewline && getToken(1).kind == WHITESPACE && getToken(2).kind == ELSEIF) })
2273      ( LOOKAHEAD( { getToken(1).kind == ELSEIF || (afterNewline && getToken(1).kind == WHITESPACE && getToken(2).kind == ELSEIF) }) ( lastBlock = ElseIfStatement(lastBlock, afterNewline) { afterNewline = lastBlock.endsWithNewline; } ))+ ]
2274    [ LOOKAHEAD( { getToken(1).kind == ELSE || (afterNewline && getToken(1).kind == WHITESPACE && getToken(2).kind == ELSE) } ) lastBlock = ElseStatement(lastBlock, afterNewline) { afterNewline = lastBlock.endsWithNewline; } ]
2275    [ LOOKAHEAD( 1, { afterNewline } ) ( t = <WHITESPACE> )
2276        {
2277            lastBlock.setPostfix(t.image);
2278            t = null;
2279        }
2280    ]
2281    (end = <END>)
2282    { afterNewline = false; }
2283    [
2284        LOOKAHEAD(2, { newlineAtStart || rsvc.getSpaceGobbling() == SpaceGobbling.BC } )
2285          ( [ ( t = <WHITESPACE> ) ] ( u = <NEWLINE> ) )
2286        {
2287             jjtThis.setPostfix(t == null ? u.image : t.image + u.image);
2288             afterNewline = true;
2289        }
2290    ]
2291    {
2292        int pos = end.image.lastIndexOf(hash);
2293        if (pos > 0)
2294        {
2295            lastBlock.setMorePostfix(end.image.substring(0, pos));
2296        }
2297        return afterNewline;
2298    }
2299}
2300
2301ASTBlock ElseStatement(ASTBlock previousBlock, boolean afterNewline) :
2302{
2303    Token t = null, u = null, _else = null;
2304    ASTBlock block = null;
2305}
2306{
2307   [ ( t = <WHITESPACE> )
2308     {
2309         previousBlock.setPostfix(t.image);
2310         t = null;
2311     }
2312   ]
2313   (_else = <ELSE>)
2314   (
2315     [
2316       LOOKAHEAD(2) ( [ ( t = <WHITESPACE> ) ] ( u = <NEWLINE> ) )
2317       {
2318           jjtThis.setPrefix(t == null ? u.image : t.image + u.image);
2319           t = u = null;
2320           afterNewline = true;
2321       }
2322     ]
2323     ( LOOKAHEAD( { getToken(1).kind != END && (!afterNewline || getToken(1).kind != WHITESPACE || getToken(2).kind != END) }) afterNewline = Statement(afterNewline) )*
2324     {
2325         block = jjtThis;
2326         block.endsWithNewline = afterNewline;
2327     }
2328    )
2329    #Block
2330    {
2331        int pos = _else.image.lastIndexOf(hash);
2332        if (pos > 0)
2333        {
2334            previousBlock.setMorePostfix(_else.image.substring(0, pos));
2335        }
2336        return block;
2337    }
2338}
2339
2340ASTBlock ElseIfStatement(ASTBlock previousBlock, boolean afterNewline) :
2341{
2342    Token t = null, u = null, elseif = null;
2343    ASTBlock block = null;
2344}
2345{
2346  [ ( t = <WHITESPACE> )
2347    {
2348        previousBlock.setPostfix(t.image);
2349        t = null;
2350    }
2351  ]
2352  (elseif = <ELSEIF>) ( <WHITESPACE> | <NEWLINE> )*
2353  <LPAREN> Expression() <RPAREN>
2354  (
2355    [
2356      LOOKAHEAD(2) ( [ ( t = <WHITESPACE> ) ] ( u = <NEWLINE> ) )
2357      {
2358          jjtThis.setPrefix(t == null ? u.image : t.image + u.image);
2359          t = u = null;
2360          afterNewline = true;
2361      }
2362    ]
2363    ( LOOKAHEAD( { (getToken(1).kind != ELSEIF && getToken(1).kind != ELSE && getToken(1).kind != END) && (!afterNewline || getToken(1).kind != WHITESPACE || (getToken(2).kind != ELSEIF && getToken(2).kind != ELSE && getToken(2).kind != END)) }) afterNewline = Statement(afterNewline) )*
2364    {
2365        block = jjtThis;
2366        block.endsWithNewline = afterNewline;
2367    }
2368  )
2369  #Block
2370  {
2371      int pos = elseif.image.lastIndexOf(hash);
2372      if (pos > 0)
2373      {
2374          previousBlock.setMorePostfix(elseif.image.substring(0, pos));
2375      }
2376      return block;
2377  }
2378}
2379
2380/**
2381 *  Currently support both types of set :
2382 *   #set( expr )
2383 *   #set expr
2384 */
2385boolean SetDirective(boolean afterNewline) :
2386{
2387    Token t = null, u = null;
2388    boolean endsWithNewline = false;
2389}
2390{
2391    [ ( t = <WHITESPACE> )
2392        {
2393            // only possible after new line
2394            jjtThis.setPrefix(t.image);
2395            t = null;
2396        }
2397    ]
2398    <SET_DIRECTIVE>(( <WHITESPACE> | <NEWLINE> )* Reference() ( <WHITESPACE> | <NEWLINE> )* <EQUALS>  Expression() <RPAREN>
2399    {
2400        /*
2401         * ensure that inSet is false.  Leads to some amusing bugs...
2402         */
2403
2404        token_source.setInSet(false);
2405    }
2406    [
2407        LOOKAHEAD(2, { afterNewline || rsvc.getSpaceGobbling() == SpaceGobbling.BC } )
2408          ( [ ( t = <WHITESPACE> ) ] ( u = <NEWLINE> ) )
2409        {
2410             jjtThis.setPostfix(t == null ? u.image : t.image + u.image);
2411             endsWithNewline = true;
2412        }
2413    ] )
2414    {
2415        return endsWithNewline;
2416    }
2417}
2418
2419/* -----------------------------------------------------------------------
2420 *
2421 *  Expression Syntax
2422 *
2423 * ----------------------------------------------------------------------*/
2424
2425void Expression() : {}
2426{
2427//    LOOKAHEAD( PrimaryExpression() <EQUALS>  ) Assignment()
2428//|
2429ConditionalOrExpression()
2430}
2431
2432void Assignment() #Assignment(2) : {}
2433{
2434    PrimaryExpression() <EQUALS>  Expression()
2435}
2436
2437void ConditionalOrExpression() #void : {}
2438{
2439  ConditionalAndExpression()
2440  (  ( <LOGICAL_OR> | <LOGICAL_OR_2> ) ConditionalAndExpression() #OrNode(2) )*
2441}
2442
2443
2444void ConditionalAndExpression() #void : {}
2445{
2446  EqualityExpression()
2447  ( <LOGICAL_AND> EqualityExpression() #AndNode(2) )*
2448}
2449
2450void EqualityExpression() #void : {}
2451{
2452    RelationalExpression()
2453    (
2454       <LOGICAL_EQUALS> RelationalExpression()     #EQNode(2)
2455     | <LOGICAL_NOT_EQUALS> RelationalExpression() #NENode(2)
2456    )*
2457}
2458
2459void RelationalExpression() #void : {}
2460{
2461    AdditiveExpression()
2462    (
2463        <LOGICAL_LT>  AdditiveExpression() #LTNode(2)
2464      | <LOGICAL_GT>  AdditiveExpression() #GTNode(2)
2465      | <LOGICAL_LE>  AdditiveExpression() #LENode(2)
2466      | <LOGICAL_GE>  AdditiveExpression() #GENode(2)
2467    )*
2468}
2469
2470void AdditiveExpression() #void : {}
2471{
2472    MultiplicativeExpression()
2473    (
2474        <PLUS>  MultiplicativeExpression() #AddNode(2)
2475      | <MINUS> MultiplicativeExpression() #SubtractNode(2)
2476    )*
2477}
2478
2479void MultiplicativeExpression() #void : {}
2480{
2481    UnaryExpression()
2482    (
2483            <MULTIPLY>  UnaryExpression() #MulNode(2)
2484          | <DIVIDE>  UnaryExpression() #DivNode(2)
2485          | <MODULUS>  UnaryExpression() #ModNode(2)
2486    )*
2487}
2488
2489void UnaryExpression() #void : {}
2490{
2491    ( <WHITESPACE> | <NEWLINE> )*
2492    (
2493          <LOGICAL_NOT>  UnaryExpression() #NotNode(1)
2494     |   <MINUS> PrimaryExpression() #NegateNode(1)
2495      |   PrimaryExpression()
2496    )
2497}
2498
2499void PrimaryExpression() #void : {}
2500{
2501    ( <WHITESPACE> | <NEWLINE> )*
2502    (
2503     StringLiteral()
2504    | Reference()
2505    | IntegerLiteral()
2506    | LOOKAHEAD(  <LBRACKET> ( <WHITESPACE> | <NEWLINE> )*    ( Reference() | IntegerLiteral())     ( <WHITESPACE> | <NEWLINE> )* <DOUBLEDOT> ) IntegerRange()
2507    | FloatingPointLiteral()
2508    | Map()
2509    | ObjectArray()
2510    | True()
2511    | False()
2512    | <LPAREN>  Expression()  <RPAREN>
2513     )
2514    ( <WHITESPACE> | <NEWLINE> )*
2515}
2516
2517/* ======================================================================
2518
2519   Notes
2520   -----
2521
2522    template == the input stream for this parser, contains 'VTL'
2523    mixed in with 'schmoo'
2524
2525    VTL == Velocity Template Language : the references, directives, etc
2526
2527    schmoo == the non-VTL component of a template
2528
2529    reference == VTL entity that represents data within the context. ex. $foo
2530
2531    directive == VTL entity that denotes 'action' (#set, #foreach, #if )
2532
2533    defined directive (DD) == VTL directive entity that is expressed
2534    explicitly w/in this grammar
2535
2536    pluggable directive (PD) == VTL directive entity that is defined outside of the
2537    grammar.  PD's allow VTL to be easily expandable w/o parser modification.
2538
2539    The problem with parsing VTL is that an input stream consists generally of
2540    little bits of VTL mixed in with 'other stuff, referred to as 'schmoo'.
2541    Unlike other languages, like C or Java, where the parser can punt whenever
2542    it encounters input that doesn't conform to the grammar, the VTL parser can't do
2543    that. It must simply output the schmoo and keep going.
2544
2545    There are a few things that we do here :
2546     - define a set of parser states (DEFAULT, DIRECTIVE, REFERENCE, etc)
2547     - define for each parser state a set of tokens for each state
2548     - define the VTL grammar, expressed (mostly) in the productions such as Text(),
2549     SetStatement(), etc.
2550
2551    It is clear that this expression of the VTL grammar (the contents
2552    of this .jjt file) is maturing and evolving as we learn more about
2553    how to parse VTL ( and as I learn about parsing...), so in the event
2554    this documentation is in disagreement w/ the source, the source
2555    takes precedence. :)
2556
2557    Parser States
2558    -------------
2559    DEFAULT :  This is the base or starting state, and strangely enough, the
2560    default state.
2561
2562    PRE_DIRECTIVE : State immediately following '#' before we figure out which
2563    defined or pluggable directive (or neither) we are working with.
2564
2565    DIRECTIVE : This state is triggered by the a match of a DD or a PD.
2566
2567    PRE_REFERENCE : Triggered by '$'. Analagous to PRE_DIRECTIVE. When '-' is
2568    allowed in identifiers, this state is called PRE_OLD_REFERENCE.
2569
2570    REFERENCE : Triggered by the <IDENTIFIER>
2571
2572    REFMODIFIER : Triggered by .<alpha> when in REFERENCE, REFMODIFIER or REFMOD3. When '-'
2573    is allowed in identifiers, this state is called OLD_REFMODIFIER.
2574
2575    REFMOD2 : Triggered by '(' when in REFMODIFIER
2576
2577    REFMOD3 : Triggered by the corresponding ')'
2578
2579    REFINDEX : Array index. Triggered by '[' in REFERENCE, REFMODIFIER, REFMOD3.
2580
2581    ALT_VAL : Alternate value. Triggered by '|'  in REFERENCE, REFMODIFIER, REFMOD3.
2582
2583    (cont)
2584
2585    Escape Sequences
2586    ----------------
2587    The escape processing in VTL is very simple.  The '\' character acts
2588    only as an escape when :
2589
2590        1) On or more touch a VTL element.
2591
2592    A VTL element is either :
2593
2594        1) It preceeds a reference that is in the context.
2595
2596        2) It preceeds a defined directive (#set, #if, #end, etc) or a valid
2597        pluggable directive, such as #foreach
2598
2599    In all other cases the '\' is just another piece of text.  The purpose of this
2600    is to allow the non-VTL parts of a template (the 'schmoo') to not have to be
2601    altered for processing by Velocity.
2602
2603    So if in the context $foo and $bar were defined and $woogie was not
2604
2605        \$foo  \$bar \$woogie
2606
2607    would output
2608
2609        $foo  $bar  \$woogie
2610
2611    Further, you can stack them and they affect left to right, just like convention
2612    escape characters in other languages.
2613
2614        \$foo = $foo
2615        \\$foo = \<foo>
2616        \\\$foo = \$foo
2617
2618
2619    What You Expect
2620    ---------------
2621    The recent versions of the parser are trying to support precise output to
2622    support general template use. The directives do not render trailing
2623    whitespace and newlines if followed by a newline.  They will render
2624    preceeding whitespace. The only exception is #set, which also eats
2625    preceeding whitespace.
2626
2627    So, with a template :
2628
2629        ------
2630        #set($foo="foo")
2631        #if($foo)
2632        \$foo = $foo
2633        #end
2634        ------
2635
2636    it will render precisely :
2637
2638        ------
2639        $foo = foo
2640        ------
2641
2642*/
2643