📄 parser.jjt
字号:
/* * Copyright 2001-2002,2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//* * NOTE : please see documentation at bottom of this file. (It was placed there its tiring * to always have to page past it... :) */options{ /** The default package for this parser kit */ NODE_PACKAGE="org.apache.velocity.runtime.parser"; /** A source file will be generated for each non-terminal */ MULTI=true; /** * Each node will have access to the parser, I did this so * some global information can be shared via the parser. I * think this will come in handly keeping track of * context, and being able to push changes back into * the context when nodes make modifications to the * context by setting properties, variables and * what not. */ NODE_USES_PARSER=true; /** * 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; /** * Enables the use of a visitor that each of nodes * will accept. This way we can separate the logic * of node processing in a visitor and out of the * nodes themselves. If processing changes then * the nothing has to change in the node code. */ VISITOR=true; /** * Declare that we are accepting unicode input and * that we are using a custom character stream class * Note that the char stream class is really a slightly * modified ASCII_CharStream, as it appears we are safe * because we only deal with pre-encoding-converted * Readers rather than raw input streams. */ UNICODE_INPUT=true; USER_CHAR_STREAM=true; /** * for debugging purposes. Keep 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.RuntimeServices;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.runtime.directive.MacroParseException;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@apache.org">Jason van Zyl</a> * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> * @version $Id: Parser.jjt,v 1.75.4.1 2004/03/03 23:22:58 geirm Exp $*/public class Parser{ /** * 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 = ""; VelocityCharStream velcharstream = null; private RuntimeServices rsvc = null; /** * 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( RuntimeServices rs) { /* * need to call the CTOR first thing. */ this( new VelocityCharStream( new ByteArrayInputStream("\n".getBytes()), 1, 1 )); /* * now setup a VCS for later use */ velcharstream = new VelocityCharStream( new ByteArrayInputStream("\n".getBytes()), 1, 1 ); /* * and save the RuntimeServices */ rsvc = rs; } /** * 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( Reader reader, String templateName ) throws ParseException { SimpleNode sn = null; currentTemplateName = templateName; try { token_source.clearStateVars(); /* * reinitialize the VelocityCharStream * with the new reader */ velcharstream.ReInit( reader, 1, 1 ); /* * now reinit the Parser with this CharStream */ ReInit( velcharstream ); /* * do that voodoo... */ sn = process(); } catch (MacroParseException mee) { /* * thrown by the Macro class when something is amiss in the * Macro specification */ rsvc.error ("Parser Error: #macro() : " + templateName + " : " + StringUtils.stackTrace(mee)); throw new ParseException(mee.getMessage()); } catch (ParseException pe) { rsvc.error ("Parser Exception: " + templateName + " : " + StringUtils.stackTrace(pe)); throw new ParseException (pe.currentToken, pe.expectedTokenSequences, pe.tokenImage); } catch (TokenMgrError tme) { throw new ParseException("Lexical error: " + tme.toString()); } catch (Exception e) { rsvc.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 ( rsvc.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, REFMOD2>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); } /* * need the LPAREN action */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -