You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@velocity.apache.org by jv...@locus.apache.org on 2000/08/31 21:27:05 UTC

cvs commit: jakarta-velocity/src/java/org/apache/velocity/parser NewParser.jjt

jvanzyl     00/08/31 12:27:04

  Added:       src/java/org/apache/velocity/parser NewParser.jjt
  Log:
  - this will be the new parser. references are dealt with cleanly.
    I have to make a minor change, then i will be able to feed a
    tree into the introspection engine. i am also going to add
    lexical states to make these work better for normal text.
    This grammar will ultimately be smaller and do more and
    should allow others to work on easily. the grammar present
    in cvs now is a bit of a dogs breakfast.
  
  Revision  Changes    Path
  1.1                  jakarta-velocity/src/java/org/apache/velocity/parser/NewParser.jjt
  
  Index: NewParser.jjt
  ===================================================================
  /*
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  options
  {
      /** The default package for this parser kit */
      NODE_PACKAGE="org.apache.velocity.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;
      
      /**
       * This option is used as one of the steps
       * required to allow the use of an "#include"
       * type behaviour. In this case the directive
       * is "#parse". See the TOKEN_MGR_DECLS section
       * below for details on how the TokenManager is
       * modified to allow this behaviour.
       */
      COMMON_TOKEN_ACTION=true;
  }    
  
  PARSER_BEGIN(Parser)
  
  package org.apache.velocity.parser;
  
  import java.io.*;
  import java.util.*;
  
  /**
   * This class is responsible for parsing a Velocity
   * template. This class was generated by JavaCC using
   * the JJTree extension to produce an Abstract
   * Syntax Tree (AST) of the template.
   *
   * Please look at the Parser.jjt file which is
   * what controls the generation of this class.
   *
   * @author <a href="mailto:jvanzyl@periapt.com">Jason van Zyl</a>
   * @version $Id: NewParser.jjt,v 1.1 2000/08/31 19:27:04 jvanzyl Exp $
   */
  public class Parser
  {
      
      
      String template;
      Hashtable context;
      SimpleNode root;
      
      public Parser(String template) throws IOException
      {
          this(new FileInputStream(template));
      }
      
      /*
      public void setContext(Hashtable context)
      {
          this.context = context;
      }
  
      public Hashtable getContext()
      {
          return context;
      } 
      */
  
      public void parse() throws ParseException
      {
          root = process();
      }
  
      public SimpleNode getRoot()
      {
          return root;
      }        
  }
  
  PARSER_END(Parser)
  
  /* ------------------------------------------------------------------------
   *
   * Tokens
   *
   * ------------------------------------------------------------------------- */
  
  SKIP : /* WHITE SPACE */
  {
    " "
  | "\t"
  | "\n"
  | "\r"
  | "\f"
  }
  
  // I'm probably going to have to add a node type to
  // deal with comments. I need to collapse the space
  // that comments take up so the formatting will be
  // more accurate.
  
  // COMMENTS
  
  SPECIAL_TOKEN : 
  {
    <SINGLE_LINE_COMMENT: "##" (~["\n","\r"])* ("\n"|"\r"|"\r\n")>
  }
  
  // DIRECTIVES 
  
  TOKEN :
  {
      <INCLUDE_DIRECTIVE: "#include">
      {
          incMode = true;
      }        
  |   <PARSE_DIRECTIVE:   "#parse">
      { 
          incMode = true; 
      }
  |   <IF_DIRECTIVE:      "#if">
  |   <ELSEIF_DIRECTIVE:  "#elseif">
  |   <ELSE_DIRECTIVE:    "#else">
  |   <FOREACH_DIRECTIVE: "#foreach">
  |   <SET_DIRECTIVE:     "#set" >
  |   <PARAM_DIRECTIVE:   "#param" >
  |   <USE_DIRECTIVE:     "#use">
  |   <STOP_DIRECTIVE:    "#stop">
      {
          matchedToken.kind = EOF;
          fileDepth = 0;
      }
  |   <STRING_LITERAL: ( "\"" ( ~["\""] )* "\"" ) >
      {
          if (incMode)
          {
              matchedToken.image = stripQuotes(image.toString());
              pushFile(matchedToken.image);
          }
          incMode = false;
      }        
  }
  
  TOKEN :
  {
      <LPAREN: "(" >
  |   <RPAREN: ")" >
  |   <LBRACKET: "{" >
  |   <RBRACKET: "}" >
  |   <EQUALS: "=" >
  |   <IN: "in" >
  |   <QUOTE: "\"">
  |   <TRUE: "true">
  |   <FALSE: "false">
  |   <DOLLAR: "$">
  |   <DOT: ".">
  |   <COMMA: ",">
  }
  
  TOKEN :
  {
    <ALPHA_CHAR: ["a"-"z", "A"-"Z"] >
  | <NUM_CHAR:   ["0"-"9"] >
  | <MONEY: <DOLLAR> (<NUM_CHAR>)* ((".")? (<NUM_CHAR>)*)? >
  | <ALPHANUM_CHAR: [ "a"-"z", "A"-"Z", "0"-"9" ] >
  | <IDENTIFIER_CHAR: [ "a"-"z", "A"-"Z", "0"-"9","-" ] >
  | <IDENTIFIER: <ALPHA_CHAR> (<IDENTIFIER_CHAR>)* >
  //| <TEXT: (~["$", "\n", "\r", "\t", " ", "(", ")"])+ >
  }
  
  /**
   * This gets inserted into the ParserMacroTokenManager
   * and is being used here strictly for the #parse
   * directive: an #include type behaviour. We have
   * to save the state the stream currently being
   * parsed and we have to save the state of the
   * lexer (TokenManager class) then we create
   * a new stream from the file named in the
   * #parse directive then we ReInit the lexer. 
   * Whatever it parses will get placed 
   * into the AST.
   *
   * I need a simple way to detect circular
   * inclusions so this thing doesn't go wild
   * and drag down the VM.
   */
  TOKEN_MGR_DECLS: 
  {  
      boolean incMode;
      int fileDepth = 0;
      int documentLine = 0;
      int lastLine = 0;
      
      Stack streams = new Stack();
      Stack states = new Stack();
      
      // remove double quotes in the string
      String stripQuotes(String str) 
      {
          int start = str.indexOf("\"");
          int end = str.indexOf("\"",start+1);    
          return str.substring(start+1,end);
      }    
      
      /**
       * Save the state of the current input stream
       * and the state of the lexer. So we can process
       * the new one.
       */
      void pushFile(String filename)
      {
          fileDepth++;
          
          streams.push(input_stream);
          states.push(new Integer(curLexState));
          documentLine += lastLine;
          
          try
          {
              FileInputStream fs = new FileInputStream(filename);
              ASCII_CharStream new_stream = new ASCII_CharStream(fs,1,1);    
              ReInit(new_stream);
          }
          catch(Exception e)
          {
          }
      }    
    
      /**
       * Retrieve the oldStream and oldState and
       * continue processing the input.
       */
      void popFile()
      {
          ReInit((ASCII_CharStream) streams.pop(), ((Integer) states.pop()).intValue());
          fileDepth--;
      }    
      
      private boolean AtParent()
      {
          if (fileDepth == 0)
              return true;
          else
              return false;
      }
      
      void CommonTokenAction(Token t)
      {
          if (t.kind == EOF && ! AtParent())
          {
              Token new_t;
              popFile();
              new_t = getNextToken();
              t.kind = new_t.kind;
              t.beginLine = new_t.beginLine;
              t.beginColumn = new_t.beginColumn;
              t.endLine = new_t.endLine;
              t.endColumn = new_t.endColumn;
              t.image = new_t.image;
              t.next = new_t.next;
              t.specialToken = new_t.specialToken;
          }
          else
          {
              lastLine = t.endLine;
              t.beginLine += documentLine;
              t.endLine += documentLine;
          }
      } 
  } 
  
  /**
   * This method is what starts the whole parsing
   * process. After the parsing is complete and
   * the template has been turned into an AST,
   * this method returns the root of AST which
   * can subsequently be traversed by a visitor
   * which implements the ParserVisitor interface
   * which is generated automatically by JavaCC
   */
  SimpleNode process() : {}
  {
     ( Statement() )* <EOF>
     { return jjtThis; }
  }
  
  /**
   * These are the types of statements that
   * are acceptable in Velocity templates.
   * I have not found that the order here
   * matters much. Someone please correct
   * me here if I'm wrong.
   */
  void Statement() #void : {}
  {
      Text()
  |   Block()    
  |   IfStatement()
  |   ElseIfStatement()
  |   ForeachStatement()
  |   IncludeStatement()
  |   SetStatement()
  |   ParseStatement()
  |   UseStatement()
  |   ParamStatement()
  |   StopStatement()
  |   Reference()
  
  }
  
  void StringLiteral() : {}
  {
      <STRING_LITERAL>
  }    
  
  /**
   * This method corresponds to variable
   * references in Velocity templates.
   * The following are examples of variable
   * references that may be found in a
   * template:
   *
   * $foo
   * $bar
   *
   */
  void Identifier() : {}
  {    
      <IDENTIFIER>
  }
  
  
  /**
   * This method corresponds to property
   * references in Velocity templates.
   * The following are examples of property
   * references that my be found in a
   * template:
   *
   * $foo.Bar
   *
   * Say the object $foo corresponds to foo
   * in the context, then the following
   * method will be invoked:
   *
   * foo.getBar()
   *
   *
  void Property() : {}
  {
      Identifier() (<DOT> Identifier())+
  }
  */
  
  /**
   * This method has yet to be fully implemented
   * but will allow arbitrarily nested method
   * calls
   */
  void Parameter() #void: {}
  {
      Reference() | StringLiteral()
  }
  
  /**
   * This method has yet to be fully implemented
   * but will allow arbitrarily nested method
   * calls
   */
  void Parameters() #void: {}
  {
      "(" [ Parameter() ( "," Parameter() )* ] ")"
  }
  
  /**
   * This method corresponds to a method
   * reference in a Velocity template.
   * The following are examples of method
   * references that may be found in a
   * template:
   *
   * $foo.getBar()
   *
   *
  void Method() : {}
  {
      Identifier Parameters()
  }
  */
  
  void Reference() : {}
  {
      "$" Identifier() (<DOT> Identifier() [ Parameters() ])*
  }
  
  /**
   * This method corresponds to a block in
   * a Velocity template. Blocks are
   * currently associated with:
   *
   * #if
   * #foreach
   * #while (not implemented)
   *
   */
  void Block() : {}
  {
    <LBRACKET> ( Statement() )* <RBRACKET>
  }
  
  /**
   * This method is responsible for allowing
   * all non-grammar text to pass through
   * unscathed.
   */
  void Text() : {}
  {
      //<TEXT>
      //<IDENTIFIER>    
  //|   <DOLLAR>
  //|   <MONEY>
     <EQUALS>
  //|   <DOT>
  //|   <QUOTE> 
  //|   <STRING_LITERAL>
  |   <IN>
  |   <TRUE>
  |   <FALSE>
  //|   <LPAREN>
  //|   <RPAREN>
  |   <COMMA>
  }
  
  /**
   * This method corresponds to an #if directive
   * in a Velocity template. The following are
   * examples if #if constructs that are
   * acceptable in a template:
   *
   * #if ($customer.owesMoney() && $customer.Name == "Fred") (not implemented)
   * {
   *     Pay up, or else!
   * }
   * #else
   * {
   *     Thankyou for your patronage.
   * }
   *
   * #if ($dynanmicContent)
   * {
   *     This is our $dynamicContent
   * }
   *
   * #if ($customer.owesMoney())
   * {
   *     You better pay up!
   * }
   *
   */
  void IfStatement() : {}
  {
    <IF_DIRECTIVE> 
    <LPAREN>
    //( <TRUE> | <FALSE> | <VARIABLE> | <METHOD> | <PROPERTY> )
    ( <TRUE> | <FALSE> | Reference() )
    <RPAREN>
    Statement()
    [ LOOKAHEAD(1) <ELSE_DIRECTIVE> Statement() ]
  }
  
  void ElseIfStatement() : {}
  {
    <ELSEIF_DIRECTIVE> 
    <LPAREN>
    //( <TRUE> | <FALSE> | <VARIABLE> | <METHOD> | <PROPERTY> )
    ( <TRUE> | <FALSE> | Reference() )
    <RPAREN>
    Statement()
    [ LOOKAHEAD(1) <ELSE_DIRECTIVE> Statement() ]
  }
  
  /**
   * This method corresponds to a #foreach
   * directive in a Velocity template. The
   * following are examples of #foreach
   * constructs that are acceptable in
   * a template:
   *
   * #foreach $element in $list
   * {
   *     This is the fifth $element
   * }
   *
   * #foreach $element in $foo.List
   * {
   *     This is the fifth $element
   * }
   *
   * #foreach $element in $foo.getList()
   * {
   *     This is the fifth $element
   * }
   *
   */
  void ForeachStatement() : {}
  {
    <FOREACH_DIRECTIVE> 
    //<VARIABLE>
    Reference()
    <IN>
    Reference()
    //( <VARIABLE> | <PROPERTY> | <METHOD> )
    Statement()
  }
  
  /**
   * This method corresponds to an #include
   * directive in a Velocity template. The
   * following are examples of #include
   * constructs that are acceptable in
   * a template:
   *
   * #include "foo.inc" 
   */
  void IncludeStatement() #void: {}
  {
    <INCLUDE_DIRECTIVE> 
    <STRING_LITERAL>
  }
  
  /**
   * This method corresponds to a #set
   * directive in a Velocity template. The
   * following are examples of #set
   * constructs that are acceptable in
   * a template:
   *
   * #set name = "Fred"
   * #set $Customer.Name = "Sidney"
   * #set $Database.findItem($partNum).Description = $newDescription (not implemented)
   *
   */
  void SetStatement() : {}
  {
    <SET_DIRECTIVE> 
    Reference()
    //( <VARIABLE> | <PROPERTY> )
    <EQUALS> 
    //( <STRING_LITERAL> | <VARIABLE> | <PROPERTY> | <METHOD> | <TRUE> | <FALSE> )
    ( <STRING_LITERAL> | Reference() | <TRUE> | <FALSE> )
  }
  
  /**
   * This method corresponds to a #parse
   * directive in a Velocity template. The
   * following are examples of #parse
   * constructs that are acceptable in
   * a template:
   *
   * #parse "bar.vt"
   */
  void ParseStatement() #void: {}
  {
    <PARSE_DIRECTIVE> <STRING_LITERAL>
  }
  
  /**
   * This method corresponds to a #use
   * directive in a Velocity template. The
   * following are examples of #use
   * constructs that are acceptable in
   * a template:
   *
   * This has not been implemented and may
   * not need to be because the Velocity
   * parser doesn't have any problem with
   * normal text that isn't part of Velocity's
   * grammar.
   *
   */
  void UseStatement() : {}
  {
    <USE_DIRECTIVE>
  }
  
  /**
   * This method corresponds to a #param
   * directive in a Velocity template. The
   * following are examples of #param
   * constructs that are acceptable in
   * a template:
   *
   * #param $language = "en"
   *
   */
  void ParamStatement() : {}
  {
    //<PARAM_DIRECTIVE> <VARIABLE> <EQUALS> <STRING_LITERAL>
    <PARAM_DIRECTIVE> Reference() <EQUALS> <STRING_LITERAL>
  }
  
  /**
   * This method corresponds to the #stop
   * directive which just simulates and EOF
   * so that parsing stops. The #stop directive
   * is really only useful for debugging
   * purposes.
   */
  void StopStatement() #void: {}
  {    
      <STOP_DIRECTIVE>
  }