/**
 *
 * Copyright (C) 1996, 1997 Sun Microsystems Inc.
 *
 * Use of this file and the system it is part of is constrained by the
 * file COPYRIGHT in the root directory of this system.  You may, however,
 * make any modifications you wish to this file.
 *
 * Java files generated by running JavaCC on this file (or modified versions
 * of this file) may be used in exactly the same manner as Java files
 * generated from any grammar developed by you.
 *
 * Author: Sriram Sankar
 * Date: 9/11/97
 *
 * This file contains a Java grammar and actions that implement a front-end.
 * This grammar is identical to Java1.1.jj, except that it uses semantic
 * lookahead instead of syntactic lookahead to cut down the size of the
 * generated parser.
 *
 * Adapted for Barat: Boris Bokowski & Andre Spiegel, 1998/06
 *
 * $Id: BaratParser.jj,v 1.79 2000/11/24 02:49:02 bokowski Exp $
 *
 */

/* magic cookie xyz
options {
  JAVA_UNICODE_ESCAPE = true;
}

PARSER_BEGIN(BaratParser)
   magic cookie xyz */

package barat.parser;

import barat.collections.QualifiedNameList;
import barat.collections.QualifiedNameIterator;
import barat.collections.QualifiedNameArrayList;
import barat.collections.FieldList;
import barat.collections.ParameterList;
import barat.*;
import barat.reflect.*;
import barat.reflect.Class;
import java.util.*;
import java.util.Stack;


public class BaratParser {

  private static Factory factory() {
    return Factory.getInstance();
  }

  static Scope   currentScope = null;

  /**
   *  Defined here because as of JDK 1.1.3, 
   *  java.lang.Long.parseLong is buggy.
   */
  private static long parseLong (String data, int radix) {
    long result = 0;
    char[] ch = data.toCharArray();
    int  length = ch.length;
    for (int i=0; i < length; i++)
      result = radix * result + Character.digit (ch[i], radix);
    return result;
  }

  /**
   *  Defined here because as of JDK 1.1.3, 
   *  java.lang.Integer.parseInt is buggy.
   */
  private static int parseInt (String data, int radix) {
    int result = 0;
    char[] ch = data.toCharArray();
    int  length = ch.length;
    for (int i=0; i < length; i++)
      result = radix * result + Character.digit (ch[i], radix);
    return result;
  }

  private static int chars_consumed;
  /**
   * Parse character literals. TODO: unicodes, etc.
   */
  private static final char parseChar(String data) {
    char[] chars = data.toCharArray();
    if(chars[0] != '\\') {
      chars_consumed = 0;
      return chars[0];
    }

    int i;
    for(i=1; (i < chars.length) && Character.isDigit(chars[i]); i++);

    if(i > 1) {
      chars_consumed = i;
      return (char)Integer.parseInt(new String(chars, 1, i-1), 8);
    }

    chars_consumed = 2;

    switch(chars[1]) {
    case 'n'  : return '\n';
    case 'r'  : return '\r';
    case 't'  : return '\t';
    case 'f'  : return '\f';
    case 'b'  : return '\b';
    case '\\' : return '\\';
    default: return (char)chars[1]; // TODO
    }
  }

  /**
   * Replace \n and the like in strings
   */
  private static final String parseString(String str) {
    int          index, old_index;
    StringBuffer buf = new StringBuffer();

    try {
      if((index = str.indexOf('\\')) != -1) { // `\' found in str
	old_index = 0;                        // String start offset
	  
	// While we have something to replace
	while((index = str.indexOf('\\', old_index)) != -1) {
	  buf.append(str.substring(old_index, index)); // append prefix
	  buf.append(parseChar(str.substring(index)));
	  old_index = index + chars_consumed;
	}

	buf.append(str.substring(old_index)); // append rest of string
	str = buf.toString();	
      }
    } catch(StringIndexOutOfBoundsException e) { // Should not occur
      System.err.println(e);
    }

    return str;
  }

  // Semantic lookahead rules follow.

  // implements:  LOOKAHEAD( ( "abstract" | "final" | "public" | "strictfp")* "class" )
  static boolean isClassDeclaration() {
    int curTok = 1;
    Token tok;
    while (true) {
      tok = getToken(curTok);
      switch (tok.kind) {
      case ABSTRACT:
      case FINAL:
      case PUBLIC:
      case STRICTFP:
        curTok++;
        break;
      case CLASS:
        return true;
      default:
        return false;
      }
    }
  }

  // implements:  LOOKAHEAD(2)  for Initializer()
  static boolean isInitializer() {
    Token tok = getToken(1);
    if (tok.kind == LBRACE) return true;
    if (tok.kind == STATIC) {
      tok = getToken(2);
      if (tok.kind == LBRACE) return true;
    }
    return false;
  }

  // implements:  LOOKAHEAD( ( "static" | "abstract" | "final" | "public" | "protected" | "private" | "strictfp" )* "class" )
  static boolean isNestedClassDeclaration() {
    int curTok = 1;
    Token tok;
    while (true) {
      tok = getToken(curTok);
      switch (tok.kind) {
      case STATIC:
      case ABSTRACT:
      case FINAL:
      case PUBLIC:
      case PROTECTED:
      case PRIVATE:
      case STRICTFP:
        curTok++;
        break;
      case CLASS:
        return true;
      default:
        return false;
      }
    }
  }

  // implements:  LOOKAHEAD( ( "static" | "abstract" | "final" | "public" | "protected" | "private" | "strictfp" )* "interface" )
  static boolean isNestedInterfaceDeclaration() {
    int curTok = 1;
    Token tok;
    while (true) {
      tok = getToken(curTok);
      switch (tok.kind) {
      case STATIC:
      case ABSTRACT:
      case FINAL:
      case PUBLIC:
      case PROTECTED:
      case PRIVATE:
      case STRICTFP:
        curTok++;
        break;
      case INTERFACE:
        return true;
      default:
        return false;
      }
    }
  }

  // implements:  LOOKAHEAD( [ "public" | "protected" | "private" | "strictfp" ] Name() "(" )
  static boolean isConstructorDeclaration() {
    int curTok = 1;
    Token tok;
    tok = getToken(1);
    switch (tok.kind) {
    case PUBLIC:
    case PROTECTED:
    case PRIVATE:
    case STRICTFP:
      curTok = 2;
    }
    tok = getToken(curTok++);
    if (tok.kind != IDENTIFIER) return false;
    while (true) {
      tok = getToken(curTok++);
      if (tok.kind == LPAREN) return true;
      if (tok.kind != DOT) return false;
      tok = getToken(curTok++);
      if (tok.kind != IDENTIFIER) return false;
    }
  }

  // Returns true if the next set of tokens cannot be a field declaration.
  // Returns false if the next set of tokens cannot be a method declaration.
  // Note how this method is used in the grammar.  We don't have to do a
  // comprehensive check like we have done in the lookahead methods above.
  // This show (therefore) another way you can implement lookahead methods.
  // The way we do it is to see if we can find a "(" before either a "=" or
  // a ";", in which case we return true.
  static boolean isMethodDeclaration() {
    int curTok = 1;
    Token tok;
    while (true) {
      tok = getToken(curTok++);
      switch (tok.kind) {
      case LPAREN:
        return true;
      case ASSIGN:
      case SEMICOLON:
      case EOF:
        return false;
      }
    }
  }

  // Checks that there is a "," and then there is no "}" following that.
  static boolean moreVariableInitializers() {
    return getToken(1).kind == COMMA && getToken(2).kind != RBRACE;
  }

  // Checks that this is a constructor invocation as opposed to a block
  // statement.
  static boolean isConstructorInvocation() {
    int curTok = 1;
    Token tok = getToken(1);
    switch (tok.kind) {
    case THIS:
    case SUPER:
      // We are assuming here that if the statement starts with "this"
      // or "super", and the next token is "(", then it has to be a
      // constructor invocation.
      return getToken(2).kind == LPAREN;
    case STRING_LITERAL:
    case LPAREN:
    case NEW:
    case IDENTIFIER:
      // Now move across tokens until the end of the statement - the
      // first semicolon not nested within any kind of parentheses.
      // If a "super(" is matched also not nested and before this
      // semicolon, we return true.  Otherwise return false.
      int nestingLevel = 0;
      while (true) {
        tok = getToken(curTok++);
        switch (tok.kind) {
        case SEMICOLON:
          if (nestingLevel == 0) {
            return false;
          }
          break;
        case SUPER:
          if (nestingLevel == 0) {
            return getToken(curTok).kind == LPAREN;
          }
          break;
        case LPAREN:
        case LBRACE:
        case LBRACKET:
          nestingLevel++;
          break;
        case RPAREN:
        case RBRACE:
        case RBRACKET:
          nestingLevel--;
          break;
        case EOF:
          return false;
        }
      }
    default:
      return false;
    }
  }

  // Returns true if this is a primitive type (or an array of primitive
  // type) cast.
  static boolean isPrimitiveTypeCast() {
    if (getToken(1).kind != LPAREN) return false;
    Token tok = getToken(2);
    switch (tok.kind) {
    case BOOLEAN:
    case CHAR:
    case BYTE:
    case SHORT:
    case INT:
    case LONG:
    case FLOAT:
    case DOUBLE:
      return true;
    }
    return false;
  }

  // Returns true if this is a type cast.
  static boolean isTypeCast() {
    if (isPrimitiveTypeCast()) return true;
    if (getToken(1).kind != LPAREN) return false;
    int curTok = 2;
    Token tok = getToken(curTok++);
    if (tok.kind != IDENTIFIER) return false;
    while (true) {
      tok = getToken(curTok++);
      if (tok.kind != DOT) break;
      tok = getToken(curTok++);
      if (tok.kind != IDENTIFIER) return false;
    }
    if (tok.kind == RPAREN) {
      tok = getToken(curTok);
      switch (tok.kind) {
      case TILDE:
      case BANG:
      case LPAREN:
      case IDENTIFIER:
      case THIS:
      case SUPER:
      case NEW:
      case INTEGER_LITERAL:
      case FLOATING_POINT_LITERAL:
      case CHARACTER_LITERAL:
      case STRING_LITERAL:
      case TRUE:
      case FALSE:
      case NULL:
        return true;
      }
      return false;
    } else {
      if (tok.kind != LBRACKET) return false;
      tok = getToken(curTok);
      if (tok.kind != RBRACKET) return false;
      return true;
    }
  }

  // Distinguishes between ClassSelector and Name.
  static boolean isClassSelector() {
    int curTok = 1;
    Token tok = getToken(curTok++);
    if (tok.kind != IDENTIFIER) return true;
    while (true) {
      tok = getToken(curTok++);
      while (tok.kind == LBRACKET) {
        // A simple loop to accept "[]"s.  We are a little sloppy
        // in that we don't require it to be at the end, but then
        // this is only a lookahead check.
        tok = getToken(curTok++);
        if (tok.kind != RBRACKET) return false;
        tok = getToken(curTok++);
      }
      if (tok.kind != DOT) return false;
      tok = getToken(curTok++);
      if (tok.kind == CLASS) return true;
      if (tok.kind != IDENTIFIER) return false;
    }
  }

  // implements:  LOOKAHEAD([ "final" ] Type() <IDENTIFIER>)
  static boolean isLocalVariableDeclaration() {
    int curTok = 1;
    Token tok = getToken(curTok++);
    if (tok.kind == FINAL) tok = getToken(curTok++);
    switch (tok.kind) {
    case BOOLEAN:
    case CHAR:
    case BYTE:
    case SHORT:
    case INT:
    case LONG:
    case FLOAT:
    case DOUBLE:
      tok = getToken(curTok++);
      break;
    case IDENTIFIER:
      while (true) {
        tok = getToken(curTok++);
        if (tok.kind != DOT) break;
        tok = getToken(curTok++);
        if (tok.kind != IDENTIFIER) return false;
      }
      break;
    default:
      return false;
    }
    while (tok.kind == LBRACKET) {
      tok = getToken(curTok++);
      if (tok.kind != RBRACKET) return false;
      tok = getToken(curTok++);
    }
    return tok.kind == IDENTIFIER;
  }

  static boolean isPrimarySuffix() {
    Token tok = getToken(1);
    if (tok.kind == LPAREN || tok.kind == LBRACKET) return true;
    if (tok.kind == DOT) {
      tok = getToken(2);
      switch (tok.kind) {
      case THIS:
      case NEW:
      case IDENTIFIER:
        return true;
      }
    }
    return false;
  }

}

PARSER_END(BaratParser)

TOKEN_MGR_DECLS : 
{
  static protected List<String> storedTags = new ArrayList<String>();
  static protected NodeImpl lastNode = null;
  static protected Scope lastScope = null;
  static protected int lastLine = -1;

  static protected boolean isPrefixTag(String s)
  {
    return s.startsWith("/*:");
  }

  static protected void addTagsFromPrefix(String s)
  {
    s = s.substring(3, s.length()-2);
    storedTags.add(s);
  }

  static protected boolean isPostfixTag(String s)
  {
    return s.startsWith("//:");
  }

  static protected void addTagsFromPostfix(String s)
  {
    s = s.substring(3, s.length()-1);
    lastNode.addTag(s, lastScope);
  }

  static protected void addTag(String t, Token matchedToken) // Called when comment is parsed
  {
    if(matchedToken.beginLine > lastLine)
    {
      lastNode = null;
      lastScope = null;
      storedTags = new ArrayList<String>();
      lastLine = matchedToken.endLine;
    }
    if(isPrefixTag(t))
    {
      addTagsFromPrefix(t);
    }
    else if(isPostfixTag(t))
    {
      addTagsFromPostfix(t);
    }
  }

  static protected void rememberTagsForExpression(Scope scope) {
    rememberTagsForExpression(scope, storedTags);
  }

  private static Stack expr_stack = new Stack();

  static protected void rememberTagsForExpression(Scope scope, List<String> stored_tags) {
    lastLine = -1;

    expr_stack.push(new Attribute.StoredTag(stored_tags, scope));
    storedTags = new ArrayList<String>();
  }

  static Attribute.StoredTag getTagsForExpression() {
    return (Attribute.StoredTag)expr_stack.pop();
  }

  static protected void setTagsFor(NodeImpl n, int line, Scope currentScope) {
    setTagsFor(n, line, currentScope, storedTags);
  }

  static protected void setTagsFor(Attribute<AExpression> expr, Attribute.StoredTag stored) {
    Attribute.stored_attribute_tags.put((Attribute)expr, stored);
  }
 
  static protected void setTagsFor(NodeImpl n, int line, Scope currentScope, List<String> stored_tags)
  {
    //if(matchedToken.beginLine > lastLine)
    //{
    //  storedTags = new ArrayList<String>();
    //}
    lastNode = n;
    lastLine = line;
    lastScope = currentScope;
    for(Iterator<String> i = stored_tags.iterator(); i.hasNext();)
    {
      String str = i.next();
      //System.out.println("Adding " + str + " to " + n);
      n.addTag(str, currentScope);
    }
    storedTags = new ArrayList<String>();
  }

  public static String trim(String line, String trimChars)
  {
	int lineLength = line.length();
	if(lineLength==0)
	{
		return "";
	}
	int lineStart = 0;
	while(trimChars.indexOf(line.charAt(lineStart))!=-1)
	{
		lineStart++;
		if(lineLength<=lineStart)
		{
			return "";
		}
	}
	return line.substring(lineStart, lineLength);
  }

  public static String trimJavadocLine(String line)
  {
  	return trim(trim(trim(line, " \t"), "*"), " \t");
  }
  
  static class MutableTag extends Tag {
 	public MutableTag(String n)
	{
		super(n, "");
	}
	public void appendValue(String v)
	{
		if(value.length()!=0) value = value + "\n";
		value = value + v;
	}
  }

  static protected void new_setTagsFor(NodeImpl n, Token token)
  {
    if(token==null) return;
    Token javadocToken = token.specialToken;
    if(javadocToken==null) return;
    String javadocString = javadocToken.image;
    if(!javadocString.startsWith("/**")) return;
    List<String> lines = new ArrayList<String>();
    java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(javadocString.substring(2,javadocString.length()-2), "\n");
    while(tokenizer.hasMoreTokens())
    {
    	String line = tokenizer.nextToken();
	lines.add(trimJavadocLine(line));
    }
    List<Tag> tags = new ArrayList<Tag>();
    MutableTag currentTag = new MutableTag("");
    for(Iterator<String> lineIterator = lines.iterator(); lineIterator.hasNext();)
    {
    	String line = lineIterator.next();
	if(!line.startsWith("@"))
	{
		currentTag.appendValue(line);
	}
	else
	{
		if(tags.size()!=0 || !currentTag.getValue().equals(""))
		{
			tags.add(currentTag);
		}
		int separatorIndex=1;
		while(separatorIndex<line.length() && " \t".indexOf(line.charAt(separatorIndex))==-1)
			separatorIndex++;
		currentTag = new MutableTag(line.substring(1, separatorIndex));
		if(separatorIndex<line.length())
		{
			currentTag.appendValue(trim(line.substring(separatorIndex), " \t")); 
		}
	}
    }
    tags.add(currentTag);
    Tag[] result = new Tag[tags.size()];
    for(int i=0; i<result.length; i++)
    {
	result[i] = tags.get(i);
}
    n.new_setTags(result);
  }
}

/* WHITE SPACE */

SKIP :
{
  " "
| "\t"
| "\n"
| "\r"
| "\f"
}

/* COMMENTS */

MORE :
{
  "//" : IN_SINGLE_LINE_COMMENT
|
  <"/**" ~["/"]> { input_stream.backup(1); } : IN_FORMAL_COMMENT
|
  "/*" : IN_MULTI_LINE_COMMENT
}

<IN_SINGLE_LINE_COMMENT>
SPECIAL_TOKEN :
{
  <SINGLE_LINE_COMMENT: "\n" | "\r" | "\r\n" > { addTag(image.toString(), matchedToken); } : DEFAULT
}

<IN_FORMAL_COMMENT>
SPECIAL_TOKEN :
{
  <FORMAL_COMMENT: "*/" > : DEFAULT
}

<IN_MULTI_LINE_COMMENT>
SPECIAL_TOKEN :
{
  <MULTI_LINE_COMMENT: "*/" > { addTag(image.toString(), matchedToken); } : DEFAULT
}

<IN_SINGLE_LINE_COMMENT,IN_FORMAL_COMMENT,IN_MULTI_LINE_COMMENT>
MORE :
{
  < ~[] >
}

/* RESERVED WORDS AND LITERALS */

TOKEN :
{
  < ABSTRACT: "abstract" >
| < BOOLEAN: "boolean" >
| < BREAK: "break" >
| < BYTE: "byte" >
| < CASE: "case" >
| < CATCH: "catch" >
| < CHAR: "char" >
| < CLASS: "class" >
| < CONST: "const" >
| < CONTINUE: "continue" >
| < _DEFAULT: "default" >
| < DO: "do" >
| < DOUBLE: "double" >
| < ELSE: "else" >
| < EXTENDS: "extends" >
| < FALSE: "false" >
| < FINAL: "final" >
| < FINALLY: "finally" >
| < FLOAT: "float" >
| < FOR: "for" >
| < GOTO: "goto" >
| < IF: "if" >
| < IMPLEMENTS: "implements" >
| < IMPORT: "import" >
| < INSTANCEOF: "instanceof" >
| < INT: "int" >
| < INTERFACE: "interface" >
| < LONG: "long" >
| < NATIVE: "native" >
| < NEW: "new" >
| < NULL: "null" >
| < PACKAGE: "package">
| < PRIVATE: "private" >
| < PROTECTED: "protected" >
| < PUBLIC: "public" >
| < RETURN: "return" >
| < SHORT: "short" >
| < STATIC: "static" >
| < SUPER: "super" >
| < SWITCH: "switch" >
| < SYNCHRONIZED: "synchronized" >
| < THIS: "this" >
| < THROW: "throw" >
| < THROWS: "throws" >
| < TRANSIENT: "transient" >
| < TRUE: "true" >
| < TRY: "try" >
| < VOID: "void" >
| < VOLATILE: "volatile" >
| < WHILE: "while" >
| < STRICTFP: "strictfp" >
}

/* LITERALS */

TOKEN :
{
  < INTEGER_LITERAL:
        <DECIMAL_LITERAL> (["l","L"])?
      | <HEX_LITERAL> (["l","L"])?
      | <OCTAL_LITERAL> (["l","L"])?
  >
|
  < #DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* >
|
  < #HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ >
|
  < #OCTAL_LITERAL: "0" (["0"-"7"])* >
|
  < FLOATING_POINT_LITERAL:
        (["0"-"9"])+ "." (["0"-"9"])* (<EXPONENT>)? (["f","F","d","D"])?
      | "." (["0"-"9"])+ (<EXPONENT>)? (["f","F","d","D"])?
      | (["0"-"9"])+ <EXPONENT> (["f","F","d","D"])?
      | (["0"-"9"])+ (<EXPONENT>)? ["f","F","d","D"]
  >
|
  < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ >
|
  < CHARACTER_LITERAL:
      "'"
      (   (~["'","\\","\n","\r"])
        | ("\\"
            ( ["n","t","b","r","f","\\","'","\""]
            | ["0"-"7"] ( ["0"-"7"] )?
            | ["0"-"3"] ["0"-"7"] ["0"-"7"]
            )
          )
      )
      "'"
  >
|
  < STRING_LITERAL:
      "\""
      (   (~["\"","\\","\n","\r"])
        | ("\\"
            ( ["n","t","b","r","f","\\","'","\""]
            | ["0"-"7"] ( ["0"-"7"] )?
            | ["0"-"3"] ["0"-"7"] ["0"-"7"]
            )
          )
      )*
      "\""
  >
}

/* IDENTIFIERS */

TOKEN :
{
  < IDENTIFIER: <LETTER> (<LETTER>|<DIGIT>)* >
|
  < #LETTER:
      [
       "\u0024",
       "\u0041"-"\u005a",
       "\u005f",
       "\u0061"-"\u007a",
       "\u00c0"-"\u00d6",
       "\u00d8"-"\u00f6",
       "\u00f8"-"\u00ff",
       "\u0100"-"\u1fff",
       "\u3040"-"\u318f",
       "\u3300"-"\u337f",
       "\u3400"-"\u3d2d",
       "\u4e00"-"\u9fff",
       "\uf900"-"\ufaff"
      ]
  >
|
  < #DIGIT:
      [
       "\u0030"-"\u0039",
       "\u0660"-"\u0669",
       "\u06f0"-"\u06f9",
       "\u0966"-"\u096f",
       "\u09e6"-"\u09ef",
       "\u0a66"-"\u0a6f",
       "\u0ae6"-"\u0aef",
       "\u0b66"-"\u0b6f",
       "\u0be7"-"\u0bef",
       "\u0c66"-"\u0c6f",
       "\u0ce6"-"\u0cef",
       "\u0d66"-"\u0d6f",
       "\u0e50"-"\u0e59",
       "\u0ed0"-"\u0ed9",
       "\u1040"-"\u1049"
      ]
  >
}

/* SEPARATORS */

TOKEN :
{
  < LPAREN: "(" >
| < RPAREN: ")" >
| < LBRACE: "{" >
| < RBRACE: "}" >
| < LBRACKET: "[" >
| < RBRACKET: "]" >
| < SEMICOLON: ";" >
| < COMMA: "," >
| < DOT: "." >
}

/* OPERATORS */

TOKEN :
{
  < ASSIGN: "=" >
| < GT: ">" >
| < LT: "<" >
| < BANG: "!" >
| < TILDE: "~" >
| < HOOK: "?" >
| < COLON: ":" >
| < EQ: "==" >
| < LE: "<=" >
| < GE: ">=" >
| < NE: "!=" >
| < SC_OR: "||" >
| < SC_AND: "&&" >
| < INCR: "++" >
| < DECR: "--" >
| < PLUS: "+" >
| < MINUS: "-" >
| < STAR: "*" >
| < SLASH: "/" >
| < BIT_AND: "&" >
| < BIT_OR: "|" >
| < XOR: "^" >
| < REM: "%" >
| < LSHIFT: "<<" >
| < RSIGNEDSHIFT: ">>" >
| < RUNSIGNEDSHIFT: ">>>" >
| < PLUSASSIGN: "+=" >
| < MINUSASSIGN: "-=" >
| < STARASSIGN: "*=" >
| < SLASHASSIGN: "/=" >
| < ANDASSIGN: "&=" >
| < ORASSIGN: "|=" >
| < XORASSIGN: "^=" >
| < REMASSIGN: "%=" >
| < LSHIFTASSIGN: "<<=" >
| < RSIGNEDSHIFTASSIGN: ">>=" >
| < RUNSIGNEDSHIFTASSIGN: ">>>=" >
}


/*****************************************
 * THE JAVA LANGUAGE GRAMMAR STARTS HERE *
 *****************************************/

/*
 * Program structuring syntax follows.
 */

CompilationUnitImpl CompilationUnit(String filename) :
{ CompilationUnitImpl unit = null;
  PackageImpl         pkg  = null;
  List<QualifiedName> classImports = new ArrayList<QualifiedName>();
  List<barat.reflect.Package>       packageImports = new ArrayList<barat.reflect.Package>();
} 
{
  [ pkg = PackageDeclaration() ]
  { if (pkg == null)
    {
      pkg = factory().getPackage (""); // the default package
    }
    packageImports.add(pkg);
  }

  ( ImportDeclaration (classImports, packageImports) )*

  {
    unit = factory().createCompilationUnit(filename, classImports, packageImports);
    unit.setEnclosingScope (pkg);
    unit.setupContainment(pkg, "compilationUnit");
    currentScope = unit;
  }

  (
    LOOKAHEAD(1, <ABSTRACT> | <CLASS> | <FINAL> | <INTERFACE> | <PUBLIC> | <SEMICOLON>)
    TypeDeclaration (unit)
  )*
  <EOF>

  { return unit; }
}

PackageImpl PackageDeclaration() :
{ QualifiedName name; }
{
  "package" name = Name(new Token[1]) ";"
  { return factory().getPackage (name.toString()); }
}

void ImportDeclaration (List<QualifiedName> classImports, List<barat.reflect.Package> packageImports) :
{ QualifiedName name;
  boolean isPackageImport = false; }
{
  "import" name = Name(new Token[1]) [ "." "*" { isPackageImport = true; } ] ";"
  {
    if (isPackageImport) {
      packageImports.add(factory().getPackage(name.toString()));
    } else if(name.toString().indexOf('.') > 0) { // Otherwise from default package -> ignore
      classImports.add(name);
    } else {
      ToBe.fixedLater("we should not ignore this because explicit imports from default package do matter");
      // details: such an import takes precedence over a package import which might have a class
      // of the same name.
    }
  }
}

void TypeDeclaration (CompilationUnitImpl compilationUnit) :
{}
{
  LOOKAHEAD( { isClassDeclaration() } )
  ClassDeclaration (compilationUnit)
|
  InterfaceDeclaration (compilationUnit)
|
  ";"
}

/*
 * Declaration syntax follows.
 */

void ClassDeclaration (CompilationUnitImpl compilationUnit) :
{ Modifiers modifiers = new Modifiers();
  Token t = null;
  Token startToken = null;
}
{
  { modifiers = factory().createModifiers(); }

  (   t="abstract"   { if(startToken==null) startToken=t; modifiers.isAbstract(true); }
    | t="final"      { if(startToken==null) startToken=t; modifiers.isFinal(true);    }
    | t="public"     { if(startToken==null) startToken=t; modifiers.isPublic(true);   }
    | t="strictfp"   { if(startToken==null) startToken=t; modifiers.isStrictfp(true); }
  )*

  UnmodifiedClassDeclaration (compilationUnit, modifiers, startToken)
}

void UnmodifiedClassDeclaration (NodeImpl      container,
                                 Modifiers modifiers, Token startToken) :
{
  ClassImpl c = null;
  Scope  enclosingScope = currentScope;
  Token  t              = null;
  Token  name           = null;
  String className      = null;
  QualifiedName     superClassName = QualifiedName.from("java.lang.Object");
  QualifiedNameList interfaceNames = null; }
{
  t="class" { if(startToken==null) startToken=t; } name = <IDENTIFIER> 
  { className   = name.toString();

    c = factory().createClass(className, container);
    c.line_number(name.beginLine);
    
    token_source.setTagsFor(c, name.endLine, currentScope);
    token_source.new_setTagsFor(c, startToken);
    c.setDelegate_modifiers (modifiers);
    c.setEnclosingScope (currentScope);
  }

  [ "extends" superClassName = Name(new Token[1]) ]
 
  { c.setSuperclass (NameAnalysis.createClassAttribute 
                                      (superClassName, currentScope)); }

  [ "implements" interfaceNames = NameList()
    {	for(QualifiedNameIterator qi = interfaceNames.iterator(); 
            qi.hasNext(); )
	  c.addImplementedInterface (NameAnalysis.createInterfaceAttribute 
                                        (qi.next(), currentScope)); }
  ]

  { currentScope = c; }

  ClassBody (c)

  { currentScope = enclosingScope; }
}

void ClassBody (ClassImpl c) :
{}
{
  "{"
    (
      LOOKAHEAD(1, <ABSTRACT> | <BOOLEAN> | <BYTE> | <CHAR> | <CLASS> | <DOUBLE> |
                   <FINAL> | <FLOAT> | <INT> | <INTERFACE> | <LONG> | <NATIVE> |
                   <PRIVATE> | <PROTECTED> | <PUBLIC> | <STRICTFP> | <SHORT> | <STATIC> |
                   <SYNCHRONIZED> | <TRANSIENT> | <VOID> | <VOLATILE> | <IDENTIFIER> |
                   <LBRACE>)

      ClassBodyDeclaration (c)
    )*
  "}"
  { if (c.getConstructors().size() == 0) {
      // add implicit default constructor
      ConstructorImpl constructor = factory().createConstructor();
      c.addConstructor (constructor);
      constructor.setEnclosingScope (currentScope);
      constructor.setDelegate_modifiers(factory().createModifiers());
      if (c.isPublic()) constructor.isPublic (true);
      ConstructorCallImpl call = NameAnalysis.createConstructorCall 
        (constructor, null, true, new ArrayList<Attribute<AExpression>>());
      constructor.setConstructorCall (call);
      BlockImpl block = factory().createBlock();
      block.setEnclosingScope(constructor);
      constructor.setBody (block);
    }
  }
}

void NestedClassDeclaration (AUserTypeImpl outerType) :
{ Modifiers modifiers = new Modifiers();
  Token t = null;
  Token startToken = null;
}
{
  { modifiers = factory().createModifiers(); }

  (   "static"    { if(startToken==null) startToken=t; modifiers.isStatic(true);    }
    | "abstract"  { if(startToken==null) startToken=t; modifiers.isAbstract(true);  }
    | "final"     { if(startToken==null) startToken=t; modifiers.isFinal(true);     }
    | "public"    { if(startToken==null) startToken=t; modifiers.isPublic(true);    }
    | "protected" { if(startToken==null) startToken=t; modifiers.isProtected(true); }
    | "private"   { if(startToken==null) startToken=t; modifiers.isPrivate(true);   }
    | "strictfp"  { if(startToken==null) startToken=t; modifiers.isStrictfp(true);  }
  )*

  UnmodifiedClassDeclaration (outerType, modifiers, startToken)
}

void ClassBodyDeclaration (ClassImpl c) :
{}
{
(  LOOKAHEAD( { isInitializer() } )
  Initializer (c)
|
  LOOKAHEAD( { isNestedClassDeclaration() } )
  NestedClassDeclaration (c)
|
  LOOKAHEAD( { isNestedInterfaceDeclaration() } )
  NestedInterfaceDeclaration (c)
|
  LOOKAHEAD( { isConstructorDeclaration() } )
  ConstructorDeclaration (c)
|
  LOOKAHEAD( { isMethodDeclaration() } )
  MethodDeclaration (c)
|
  FieldDeclaration (c)
)
[";"]
}

void InterfaceDeclaration (CompilationUnitImpl compilationUnit) :
{ Modifiers modifiers = new Modifiers();
  Token startToken = null;
  Token t = null;
}
{
  { modifiers = factory().createModifiers(); }

  (   t="abstract" { if(startToken==null) startToken=t; modifiers.isAbstract(true); }
    | t="public"   { if(startToken==null) startToken=t; modifiers.isPublic(true);   }
    | t="strictfp" { if(startToken==null) startToken=t; modifiers.isStrictfp(true); }
  )*

  UnmodifiedInterfaceDeclaration (compilationUnit, modifiers, startToken)
}

void NestedInterfaceDeclaration (AUserTypeImpl outerType) :
{ Modifiers modifiers = new Modifiers();
  Token startToken = null;
  Token t = null;
}
{
  { modifiers = factory().createModifiers(); }

  (   t="static"    { if(startToken==null) startToken=t; modifiers.isStatic(true);    }
    | t="abstract"  { if(startToken==null) startToken=t; modifiers.isAbstract(true);  }
    | t="final"     { if(startToken==null) startToken=t; modifiers.isFinal(true);     }
    | t="public"    { if(startToken==null) startToken=t; modifiers.isPublic(true);    }
    | t="protected" { if(startToken==null) startToken=t; modifiers.isProtected(true); }
    | t="private"   { if(startToken==null) startToken=t; modifiers.isPrivate(true);   }
    | t="strictfp"  { if(startToken==null) startToken=t; modifiers.isStrictfp(true);  }
  )*

  UnmodifiedInterfaceDeclaration (outerType, modifiers, startToken)
}

void UnmodifiedInterfaceDeclaration (NodeImpl      container,
                                     Modifiers modifiers, Token startToken) :
{
  InterfaceImpl i = null;
  Scope  enclosingScope     = currentScope;
  Token  t                  = null;
  Token  name               = null;
  String interfaceName      = null;
  QualifiedNameList superInterfaceNames = new QualifiedNameArrayList();
}
{
  t="interface" { if(startToken==null) startToken=t; } name = <IDENTIFIER> 
  { interfaceName = name.toString();

    i = factory().createInterface(interfaceName, container);
    i.line_number(name.beginLine);

    token_source.setTagsFor(i, name.endLine, currentScope);
    token_source.new_setTagsFor(i, startToken);
    i.setDelegate_modifiers (modifiers);
    i.setEnclosingScope (currentScope);
  }

  [ "extends" superInterfaceNames = NameList() ]

  { for(QualifiedNameIterator qi = superInterfaceNames.iterator(); 
        qi.hasNext(); )
    i.addExtendedInterface (NameAnalysis.createInterfaceAttribute 
                            (qi.next(), currentScope)); 
  }

  "{"
    { currentScope = i; }
    (
      LOOKAHEAD(1, <ABSTRACT> | <BOOLEAN> | <BYTE> | <CHAR> | <CLASS> | <DOUBLE> |
                   <FINAL> | <FLOAT> | <INT> | <INTERFACE> | <LONG> | <NATIVE> |
                   <PRIVATE> | <PROTECTED> | <PUBLIC> | <STRICTFP> | <SHORT> | <STATIC> |
                   <SYNCHRONIZED> | <TRANSIENT> | <VOID> | <VOLATILE> | <IDENTIFIER>)
      InterfaceMemberDeclaration (i)
    )*
  "}"
  { currentScope = enclosingScope; }
}

void InterfaceMemberDeclaration (InterfaceImpl i) :
{}
{
(  LOOKAHEAD( { isNestedClassDeclaration() } )
  NestedClassDeclaration (i)
|
  LOOKAHEAD( { isNestedInterfaceDeclaration() } )
  NestedInterfaceDeclaration (i)
|
  LOOKAHEAD( { isMethodDeclaration() } )
  MethodDeclaration (i)
|
  FieldDeclaration (i)
)
[";"]
}

void FieldDeclaration (AUserTypeImpl outerType) :
{ Modifiers modifiers = new Modifiers();
  Token t = null;
  Token startToken = null;
  Token[] tHolder = new Token[1];
  Attribute<AType>     type;
  NodeImpl      item;
  FieldList               result;
}
{
  { modifiers = factory().createModifiers(); }

  (   t="public"     { if(startToken==null) startToken=t; modifiers.isPublic(true);    }
    | t="protected"  { if(startToken==null) startToken=t; modifiers.isProtected(true); }
    | t="private"    { if(startToken==null) startToken=t; modifiers.isPrivate(true);   }
    | t="static"     { if(startToken==null) startToken=t; modifiers.isStatic(true);    }
    | t="final"      { if(startToken==null) startToken=t; modifiers.isFinal(true);     }
    | t="transient"  { if(startToken==null) startToken=t; modifiers.isTransient(true); }
    | t="volatile"   { if(startToken==null) startToken=t; modifiers.isVolatile(true);  }
  )*
  type = Type(tHolder) { if(startToken==null) startToken=tHolder[0]; }
  item = VariableDeclarator (type, false, startToken) 
  { ((FieldImpl)item).setDelegate_modifiers (modifiers);
    outerType.addField (((FieldImpl)item));
    if(outerType instanceof Interface)
    {
      modifiers.isPublic(true);
      modifiers.isStatic(true);
      modifiers.isFinal(true);
    }
  }
  ( "," item = VariableDeclarator (type, false, startToken) 
        { ((FieldImpl)item).setDelegate_modifiers (modifiers);
          outerType.addField (((FieldImpl)item));
        }
  )* ";"
}

NodeImpl VariableDeclarator (Attribute<AType> type,
                                       boolean          isVariable, Token startToken) :
{ String id;
  Attribute<AExpression> initializer;
  int lineHolder[] = new int[1];
  FieldImpl field = null;
  LocalVariableImpl v = null;
}
{
  id = VariableDeclaratorId(lineHolder)

  { while (id.endsWith ("[]")) {
      id        = id.substring (0, id.length()-2);
      type      = NameAnalysis.createArrayAttribute(type);
    }
  }

  { if (!isVariable) {
      field = factory().createField();
      field.setType (type);
      field.setName (id);
      field.line_number(lineHolder[0]);
      token_source.setTagsFor(field, lineHolder[0], currentScope);
      token_source.new_setTagsFor(field, startToken);
    } else {
      v = factory().createLocalVariable();
      v.setType (type);
      v.setName (id);
      v.line_number(lineHolder[0]);
      token_source.setTagsFor(v, lineHolder[0], currentScope);
      token_source.new_setTagsFor(v, startToken);
    }
    initializer = null;
  }
  [ "=" initializer = VariableInitializer(true) ]

  { if (!isVariable) {
      if (initializer != null)
      {
        field.setInitializer (initializer);
      }
      return field;
    } else {
      if (initializer != null)
      {
        v.setInitializer (initializer);
      }
      return v;
    }
  }
}

String VariableDeclaratorId(int[] lineHolder) :
{ Token t;
  String result; 
}
{
  t = <IDENTIFIER>
  { lineHolder[0] = t.endLine; result = t.image; }

  ( "[" "]" { result = result + "[]"; } )*

  { return result; }
}

Attribute<AExpression> VariableInitializer(boolean wrapArrayInitializer) :
{ Attribute<AExpression> result;
}
{
  result = ArrayInitializer()  {
    if(wrapArrayInitializer) result = factory().wrapArrayInitializer(result);
    return result;
  }
|
  LOOKAHEAD(1, <BOOLEAN> | <BYTE> | <CHAR> | <DOUBLE> | <FALSE> | <FLOAT> | <INT> |
               <LONG> | <NEW> | <NULL> | <SHORT> | <SUPER> | <THIS> | <TRUE> |
               <VOID> | <INTEGER_LITERAL> | <FLOATING_POINT_LITERAL> |
               <CHARACTER_LITERAL> | <STRING_LITERAL> | <IDENTIFIER> | <LPAREN> |
               <BANG> | <TILDE> | <INCR> | <DECR> | <PLUS> | <MINUS>)
  result = Expression()        { return result; }
}

Attribute<AExpression> ArrayInitializer() :
{ ArrayInitializerImpl result;
  Attribute<AExpression>     item;
}
{
  "{"
    { result = factory().createArrayInitializer();
      result.line_number(token.beginLine);
    }
    [
      LOOKAHEAD(1, <BOOLEAN> | <BYTE> | <CHAR> | <DOUBLE> | <FALSE> | <FLOAT> | <INT> |
                   <LONG> | <NEW> | <NULL> | <SHORT> | <SUPER> | <THIS> | <TRUE> |
                   <VOID> | <INTEGER_LITERAL> | <FLOATING_POINT_LITERAL> |
                   <CHARACTER_LITERAL> | <STRING_LITERAL> | <IDENTIFIER> | <LPAREN> |
                   <LBRACE> | <BANG> | <TILDE> | <INCR> | <DECR> | <PLUS> | <MINUS>)
      item = VariableInitializer(false)
      { result.addArgument (item); }
      ( LOOKAHEAD( { getToken(1).kind == COMMA && getToken(2).kind != RBRACE } )
        "," item = VariableInitializer(false)
            { result.addArgument (item); }
      )*
    ]
    [ "," ]
  "}"
  { return new Constant<AExpression> (result); }
}

void MethodDeclaration (AUserTypeImpl outerType) :
{ AMethodImpl   m;
  Modifiers modifiers = new Modifiers();
  Token t = null;
  Token startToken = null;
  Token[] tHolder = new Token[1];
  Attribute<AType>        resultType;
  Scope                   enclosingScope = currentScope;
  String                  name;
  ParameterList           parameters;
  QualifiedNameList       exceptionNames;
  BlockImpl		  body;
}
{
  { modifiers      = factory().createModifiers(); }
  (   t="public"       { if(startToken==null) startToken=t; modifiers.isPublic(true);       }
    | t="protected"    { if(startToken==null) startToken=t; modifiers.isProtected(true);    }
    | t="private"      { if(startToken==null) startToken=t; modifiers.isPrivate(true);      }
    | t="static"       { if(startToken==null) startToken=t; modifiers.isStatic(true);       }
    | t="abstract"     { if(startToken==null) startToken=t; modifiers.isAbstract(true);     } 
    | t="final"        { if(startToken==null) startToken=t; modifiers.isFinal(true);        }
    | t="native"       { if(startToken==null) startToken=t; modifiers.isNative(true);       }
    | t="synchronized" { if(startToken==null) startToken=t; modifiers.isSynchronized(true); }
    | t="strictfp"     { if(startToken==null) startToken=t; modifiers.isStrictfp(true);     }
  )*
  resultType = ResultType(tHolder) 

  { if(startToken==null) startToken=tHolder[0];
    if (   (outerType instanceof InterfaceImpl)
        || modifiers.isAbstract() || modifiers.isNative()) {
      if(outerType instanceof InterfaceImpl)
      {
        modifiers.isPublic(true);
        modifiers.isAbstract(true);
      }
      m = factory().createAbstractMethod();
      m.setDelegate_modifiers (modifiers);
      token_source.new_setTagsFor(m, startToken);
      if (resultType != null) m.setResultType (resultType);
      m.setEnclosingScope (outerType);
    } else {
      m = factory().createConcreteMethod();
      m.setDelegate_modifiers (modifiers);
      token_source.new_setTagsFor(m, startToken);
      if (resultType != null) m.setResultType (resultType);
      m.setEnclosingScope (outerType);
    }
  }

  MethodDeclarator (m, resultType) 

  [ "throws" exceptionNames = NameList() 
    {	for(QualifiedNameIterator qi = exceptionNames.iterator(); 
            qi.hasNext(); )
	  m.addException (NameAnalysis.createClassAttribute 
                                        (qi.next(), currentScope)); }
  ]

  { currentScope = m; }

  (   body = Block() 
      { ((ConcreteMethodImpl)m).setBody (body); 
        ((ClassImpl)outerType).addConcreteMethod ((ConcreteMethodImpl)m); 
      }
    | ";"
      { outerType.addAbstractMethod ((AbstractMethodImpl)m); }
  )

  { currentScope = enclosingScope; }
}

void MethodDeclarator (AMethodImpl m,
                       Attribute<AType> resultType) :
{ Token t; }
{
  t = <IDENTIFIER> 
  { m.setName (t.image); token_source.setTagsFor(m, t.endLine, currentScope); m.line_number(t.beginLine); }

  FormalParameters (m)

  ( "[" "]" 
    { m.setResultType (NameAnalysis.createArrayAttribute(resultType)); 
    } 
  )*

}

void FormalParameters (AMethodImpl m) :
{ ParameterImpl p; }
{
  "(" [ p = FormalParameter()
        { m.addParameter (p); } 
        ( "," p = FormalParameter()
              { m.addParameter (p); } 
        )* 
      ] 
  ")"
}

ParameterImpl FormalParameter() :
{ Attribute<AType> type; 
  String id;
  Token t = null;
  Token startToken = null;
  Token[] tHolder = new Token[1];
  int lineHolder[] = new int[1];
  boolean is_final = false;
}
{
  [ t="final" { startToken = t; is_final = true; } ] 
  type = Type(tHolder) { if(startToken==null) startToken=tHolder[0]; } 
  id = VariableDeclaratorId(lineHolder)
  { 
    while (id.endsWith ("[]")) {
      type = NameAnalysis.createArrayAttribute(type);
      id   = id.substring (0, id.length()-2);
    }
  }
  {
    ParameterImpl result = factory().createParameter (type, id);
    token_source.setTagsFor(result, lineHolder[0], currentScope);
    result.line_number(lineHolder[0]);
    result.isFinal(is_final);
    return result;
  }
}

void ConstructorDeclaration (ClassImpl c) :
{ Modifiers   modifiers = new Modifiers(); 
  ConstructorImpl constructor;
  BlockImpl       body;
  Scope           enclosingScope = currentScope;
  Token t = null;
  Token startToken = null;
  QualifiedNameList exceptionNames;
}
{
  { modifiers = factory().createModifiers(); }
  [   t="public"    { if(startToken==null) startToken = t; modifiers.isPublic(true);    }
    | t="protected" { if(startToken==null) startToken = t; modifiers.isProtected(true); }
    | t="private"   { if(startToken==null) startToken = t; modifiers.isPrivate(true);   }
    | t="strictfp"  { if(startToken==null) startToken = t; modifiers.isStrictfp(true);  }
  ]

  t = <IDENTIFIER> 
  { if(startToken==null) startToken = t; 
    constructor = factory().createConstructor(); 
    constructor.setDelegate_modifiers (modifiers);
    constructor.setName ("<init>");
    token_source.setTagsFor(constructor, t.beginLine, currentScope);
    token_source.new_setTagsFor(constructor, startToken);
    constructor.line_number(t.beginLine);
    constructor.setEnclosingScope (c);
    c.addConstructor (constructor);
  }

  FormalParameters (constructor) 

  [ "throws" exceptionNames = NameList() 
    {	for(QualifiedNameIterator qi = exceptionNames.iterator(); 
            qi.hasNext(); )
	  constructor.addException (NameAnalysis.createClassAttribute 
                                        (qi.next(), currentScope)); }
  ]

  "{"

  { body         = factory().createBlock();
    body.setEnclosingScope(constructor);
    body.line_number(token.beginLine);
    currentScope = body; 
  }

    [ LOOKAHEAD( { isConstructorInvocation() } ) 
      ExplicitConstructorInvocation (constructor) 
    ]

    { if (constructor.getConstructorCall() == null) {
        // add default invocation of super class constructor
        ConstructorCallImpl call = NameAnalysis.createConstructorCall
          (constructor, null, true, new ArrayList<Attribute<AExpression>>());
        constructor.setConstructorCall (call);
      }
    }

    (
      LOOKAHEAD(1, <BOOLEAN> | <BREAK> | <BYTE> | <CHAR> | <CLASS> | <CONTINUE> |
                   <DO> | <DOUBLE> | <FALSE> | <FINAL> | <FLOAT> | <FOR> | <IF> |
                   <INT> | <LONG> | <NEW> | <NULL> | <RETURN> | <SHORT> | <SUPER> |
                   <SWITCH> | <SYNCHRONIZED> | <THIS> | <THROW> | <TRUE> | <TRY> |
                   <VOID> | <WHILE> | <INTEGER_LITERAL> | <FLOATING_POINT_LITERAL> |
                   <CHARACTER_LITERAL> | <STRING_LITERAL> | <IDENTIFIER> | <LPAREN> |
                   <LBRACE> | <SEMICOLON> | <INCR> | <DECR>)
      BlockStatement (body)
    )*
  "}"
  { constructor.setBody (body);
    currentScope = enclosingScope; }
}

void ExplicitConstructorInvocation (ConstructorImpl constructor) :
{ Attribute<AExpression>       prefix = null;
  List<Attribute<AExpression>> arguments;
}
{
  LOOKAHEAD( { getToken(1).kind == THIS && getToken(2).kind == LPAREN } )
  "this" arguments = Arguments() ";"
  { constructor.setConstructorCall 
      (NameAnalysis.createConstructorCall (currentScope, null, false, 
                                           arguments));
  }
|
  LOOKAHEAD(1, <BOOLEAN> | <BYTE> | <CHAR> | <DOUBLE> | <FALSE> | <FLOAT> | <INT> |
               <LONG> | <NEW> | <NULL> | <SHORT> | <SUPER> | <THIS> | <TRUE> |
               <VOID> | <INTEGER_LITERAL> | <FLOATING_POINT_LITERAL> |
               <CHARACTER_LITERAL> | <STRING_LITERAL> | <IDENTIFIER> | <LPAREN>)
  [
    LOOKAHEAD( { getToken(1).kind != SUPER || getToken(2).kind != LPAREN } )
    prefix = PrimaryExpression() "."
  ]
  "super" arguments = Arguments() ";"
  { constructor.setConstructorCall (
     (NameAnalysis.createConstructorCall (currentScope, prefix, true, 
                                          arguments)));
  }
}

void Initializer (ClassImpl c) :
{ BlockImpl block;
  boolean isStatic = false;
}
{
  [ "static" { isStatic = true; } ] block = Block()
  {
    if(isStatic)
    {
      c.addStaticInitializer (block);
    }
    else
    {
      c.addInstanceInitializer (block);
    }
  }
}


/*
 * Type, name and expression syntax follows.
 */

Attribute<AType> Type(Token[] tHolder) :
{ QualifiedName name;
  ATypeImpl type;
  Attribute<AType>    result; }
{
  (   type = PrimitiveType(tHolder) 
      { result = NameAnalysis.asAttribute (type); }
    | name = Name(tHolder) 
      { result = NameAnalysis.createUserTypeAttribute (name, currentScope); }
  ) 

  ( "[" "]" 
    { result = NameAnalysis.createArrayAttribute(result); }
  )*

  { return result; }
}

PrimitiveTypeImpl PrimitiveType(Token[] tHolder) :
{Token t = null;}
{
  t="boolean"  { tHolder[0]=t; return factory().getBoolean(); }
|
  t="char"     { tHolder[0]=t; return factory().getChar();    }
|
  t="byte"     { tHolder[0]=t; return factory().getByte();    }
|
  t="short"    { tHolder[0]=t; return factory().getShort();   }
|
  t="int"      { tHolder[0]=t; return factory().getInt();     }
|
  t="long"     { tHolder[0]=t; return factory().getLong();    }
|
  t="float"    { tHolder[0]=t; return factory().getFloat();   }
|
  t="double"   { tHolder[0]=t; return factory().getDouble();  }
}

Attribute<AType> ResultType(Token[] tHolder) :
{ Attribute<AType> type;
  Token t = null;
}
{
  t="void"        { tHolder[0]=t; return null; }
|
  type = Type(tHolder) { return type; }
}

QualifiedName Name(Token[] tHolder) :
{ Token t;
  QualifiedName result;
}
{
  t = <IDENTIFIER>
  { tHolder[0] = t;
    result = QualifiedName.from (t.image); }

  ( LOOKAHEAD( { getToken(1).kind == DOT && getToken(2).kind == IDENTIFIER } )
    "." t = <IDENTIFIER>
    { result = new QualifiedName (t.image, result); }
  )*
  { return result; }
}

QualifiedNameList NameList() :
{ QualifiedNameList result;
  QualifiedName     item;
}
{
  { result = new QualifiedNameArrayList(); }
  item = Name(new Token[1])
  { result.add (item); }
  ( "," item = Name(new Token[1])
        { result.add (item); }
  )*
  { return result; }
}


/*
 * Expression syntax follows.
 */

Attribute<AExpression> Expression() :
/*
 * This expansion has been written this way instead of:
 *   Assignment() | ConditionalExpression()
 * for performance reasons.
 * However, it is a weakening of the grammar for it allows the LHS of
 * assignments to be any conditional expression whereas it can only be
 * a primary expression.  Consider adding a semantic predicate to work
 * around this.
 */
{ Attribute<AExpression> result;
  Attribute<AExpression> rightOperand;
  String operator; 
}
{
  result = ConditionalExpression()
  [ 
    operator     = AssignmentOperator() 
    rightOperand = Expression()
    { result = NameAnalysis.asAttribute 
                 (factory().createAssignment (result, operator, rightOperand));
    }
  ]
  { return result; }
}

String AssignmentOperator() :
{}
{
  "="     { return "=";    } 
| "*="    { return "*=";   } 
| "/="    { return "/=";   } 
| "%="    { return "%=";   } 
| "+="    { return "+=";   } 
| "-="    { return "-=";   } 
| "<<="   { return "<<=";  } 
| ">>="   { return ">>=";  } 
| ">>>="  { return ">>>="; } 
| "&="    { return "&=";   } 
| "^="    { return "^=";   } 
| "|="    { return "|=";   } 
}

Attribute<AExpression> ConditionalExpression() :
{ Attribute<AExpression> ifTrue;
  Attribute<AExpression> ifFalse;
  Attribute<AExpression> result;
}
{
  result = ConditionalOrExpression() 
  [ "?" ifTrue = Expression() 
    ":" ifFalse = ConditionalExpression() 
    { result = NameAnalysis.asAttribute 
                 (factory().createConditional (result, ifTrue, ifFalse));
    }
  ]
  { return result; }
}

Attribute<AExpression> ConditionalOrExpression() :
{ Attribute<AExpression> rightOperand;
  Attribute<AExpression> result;
  String operator;
}
{
  result = ConditionalAndExpression() 
  ( "||"  { operator = "||";  }
    rightOperand = ConditionalAndExpression()
    { result = NameAnalysis.asAttribute
                 (factory().createBinaryOperation 
                    (result, operator, rightOperand));
    }
  )*
  { return result; }
}

Attribute<AExpression> ConditionalAndExpression() :
{ Attribute<AExpression> rightOperand;
  Attribute<AExpression> result;
  String operator;
}
{
  result = InclusiveOrExpression() 
  ( "&&"  { operator = "&&";  }
    rightOperand = InclusiveOrExpression()
    { result = NameAnalysis.asAttribute
                 (factory().createBinaryOperation 
                   (result, operator, rightOperand));
    }
  )*
  { return result; }
}

Attribute<AExpression> InclusiveOrExpression() :
{ Attribute<AExpression> rightOperand;
  Attribute<AExpression> result;
  String operator;
}
{
  result = ExclusiveOrExpression() 
  ( "|"  { operator = "|";  }
    rightOperand = ExclusiveOrExpression()
    { result = NameAnalysis.asAttribute
                 (factory().createBinaryOperation 
                   (result, operator, rightOperand));
    }
  )*
  { return result; }
}

Attribute<AExpression> ExclusiveOrExpression() :
{ Attribute<AExpression> rightOperand;
  Attribute<AExpression> result;
  String operator;
}
{
  result = AndExpression() 
  ( "^"  { operator = "^";  }
    rightOperand = AndExpression()
    { result = NameAnalysis.asAttribute
                 (factory().createBinaryOperation 
                   (result, operator, rightOperand));
    }
  )*
  { return result; }
}

Attribute<AExpression> AndExpression() :
{ Attribute<AExpression> rightOperand;
  Attribute<AExpression> result;
  String operator;
}
{
  result = EqualityExpression() 
  ( "&"  { operator = "&";  }
    rightOperand = EqualityExpression()
    { result = NameAnalysis.asAttribute
                 (factory().createBinaryOperation 
                   (result, operator, rightOperand));
    }
  )*
  { return result; }
}

Attribute<AExpression> EqualityExpression() :
{ Attribute<AExpression> rightOperand;
  Attribute<AExpression> result;
  String operator;
}
{
  result = InstanceOfExpression() 
  ( (   "=="  { operator = "==";  }
      | "!="  { operator = "!=";  }
    ) 
    rightOperand = InstanceOfExpression()
    { result = NameAnalysis.asAttribute
                 (factory().createBinaryOperation 
                   (result, operator, rightOperand));
    }
  )*
  { return result; }
}

Attribute<AExpression> InstanceOfExpression() :
{ Attribute<AType> type;
  Attribute<AExpression>    result;
 }
{
  result = RelationalExpression() 
  [ "instanceof" type = Type(new Token[1]) 
    { result = NameAnalysis.asAttribute
                 (factory().createInstanceof (result, type)); }
  ]
  { return result; }
}

Attribute<AExpression> RelationalExpression() :
{ Attribute<AExpression> rightOperand;
  Attribute<AExpression> result;
  String operator;
}
{
  result = ShiftExpression() 
  ( (   "<"  { operator = "<";  }
      | ">"  { operator = ">";  }
      | "<=" { operator = "<="; }
      | ">=" { operator = ">="; }
    ) 
    rightOperand = ShiftExpression()
    { result = NameAnalysis.asAttribute
                 (factory().createBinaryOperation 
                   (result, operator, rightOperand));
    }
  )*
  { return result; }
}

Attribute<AExpression> ShiftExpression() :
{ Attribute<AExpression> rightOperand;
  Attribute<AExpression> result;
  String operator;
}
{
  result = AdditiveExpression() 
  ( (   "<<"  { operator = "<<";  }
      | ">>"  { operator = ">>";  }
      | ">>>" { operator = ">>>"; }
    ) 
    rightOperand = AdditiveExpression()
    { result = NameAnalysis.asAttribute
                 (factory().createBinaryOperation 
                   (result, operator, rightOperand));
    }
  )*
  { return result; }
}

Attribute<AExpression> AdditiveExpression() :
{ Attribute<AExpression> rightOperand;
  Attribute<AExpression> result;
  String operator;
}
{
  result = MultiplicativeExpression() 
  ( (   "+"  { operator = "+";  }
      | "-"  { operator = "-";  }
    ) 
    rightOperand = MultiplicativeExpression()
    { result = NameAnalysis.asAttribute
                 (factory().createBinaryOperation 
                   (result, operator, rightOperand));
    }
  )*
  { return result; }
}

Attribute<AExpression> MultiplicativeExpression() :
{ Attribute<AExpression> rightOperand;
  Attribute<AExpression> result;
  String operator;
}
{
  result = UnaryExpression() 
  ( (   "*"  { operator = "*";  }
      | "/"  { operator = "/";  }
      | "%"  { operator = "%";  }
    ) 
    rightOperand = UnaryExpression()
    { result = NameAnalysis.asAttribute
                 (factory().createBinaryOperation 
                   (result, operator, rightOperand));
    }
  )*
  { return result; }
}

Attribute<AExpression> UnaryExpression() :
{ String operator;
  Attribute<AExpression> operand; }
{
  (   "+" { operator = "+"; }
    | "-" { operator = "-"; }
  ) operand = UnaryExpression()
  { return NameAnalysis.asAttribute 
             (factory().createUnaryOperation (operand, operator, false)); 
  }
|
  operand = PreIncrementExpression()
  { return operand; }
|
  operand = PreDecrementExpression()
  { return operand; }
|
  LOOKAHEAD(1, <BOOLEAN> | <BYTE> | <CHAR> | <DOUBLE> | <FALSE> | <FLOAT> |
               <INT> | <LONG> | <NEW> | <NULL> | <SHORT> | <SUPER> | <THIS> |
               <TRUE> | <VOID> | <INTEGER_LITERAL> | <FLOATING_POINT_LITERAL> |
               <CHARACTER_LITERAL> | <STRING_LITERAL> | <IDENTIFIER> |
               <LPAREN> | <BANG> | <TILDE>)
  operand = UnaryExpressionNotPlusMinus()
  { return operand; }
}

Attribute<AExpression> PreIncrementExpression() :
{ Attribute<AExpression> operand; }
{
  "++" operand = PrimaryExpression()
  { return NameAnalysis.asAttribute 
             (factory().createUnaryOperation (operand, "++", false)); 
  }
}

Attribute<AExpression> PreDecrementExpression() :
{ Attribute<AExpression> operand; }
{
  "--" operand = PrimaryExpression()
  { return NameAnalysis.asAttribute
             (factory().createUnaryOperation (operand, "--", false)); 
  }
}

Attribute<AExpression> UnaryExpressionNotPlusMinus() :
{ String operator;
  Attribute<AExpression> operand; }
{
  (   "~" { operator = "~"; }
    | "!" { operator = "!"; }
  ) operand = UnaryExpression()
  { return NameAnalysis.asAttribute 
             (factory().createUnaryOperation (operand, operator, false)); 
  }
|
  LOOKAHEAD( { isTypeCast() } )
  operand = CastExpression()
  { return operand; }
|
  LOOKAHEAD(1, <BOOLEAN> | <BYTE> | <CHAR> | <DOUBLE> | <FALSE> | <FLOAT> |
               <INT> | <LONG> | <NEW> | <NULL> | <SHORT> | <SUPER> | <THIS> |
               <TRUE> | <VOID> | <INTEGER_LITERAL> | <FLOATING_POINT_LITERAL> |
               <CHARACTER_LITERAL> | <STRING_LITERAL> | <IDENTIFIER> | <LPAREN>)
  operand = PostfixExpression()
  { return operand; }
}

Attribute<AExpression> PostfixExpression() :
{ Attribute<AExpression> operand;
}
{
  operand = PrimaryExpression() 
  [   "++" { operand = NameAnalysis.asAttribute 
                         (factory().createUnaryOperation (operand, "++", true));
           }
    | "--" { operand = NameAnalysis.asAttribute
                         (factory().createUnaryOperation (operand, "--", true));
           }
  ]
  { return operand; }
}

Attribute<AExpression> CastExpression() :
{ Attribute<AExpression> operand;
  Attribute<AType>       castType;
}
{
  LOOKAHEAD( { isPrimitiveTypeCast() } )
  "(" castType = Type(new Token[1]) ")" operand = UnaryExpression()
  { return NameAnalysis.asAttribute
             (factory().createCast (operand, castType)); 
  }
|
  "(" castType = Type(new Token[1]) ")" operand = UnaryExpressionNotPlusMinus()
  { return NameAnalysis.asAttribute
             (factory().createCast (operand, castType)); 
  }
}

Attribute<AExpression> PrimaryExpression() :
{ Attribute<AExpression> prefixExpression = null;
  java.lang.Object       prefix, 
                         suffix;
  QualifiedName          qname;
}
{
  prefix = PrimaryPrefix() 
  { if (prefix instanceof QualifiedName) {
      prefixExpression = null;
      qname             = (QualifiedName)prefix;
    } else {
      prefixExpression = (Attribute)prefix;
      qname             = null;
    }
  }    

  ( LOOKAHEAD( { isPrimarySuffix() } ) 
    suffix = PrimarySuffix()
    { if (suffix instanceof String) {
        if(suffix.equals("this"))
        {
          if(prefixExpression!=null) throw new RuntimeException(".this may only occur after a qualified name");
          ThisImpl newThis = factory().createThis();
          newThis.setThisClass(NameAnalysis.createUserTypeAttributeAsUserType(qname, currentScope));
          prefixExpression = NameAnalysis.asAttribute(newThis);
          qname = null;
        }
        else if(suffix.equals("super"))
        {
          if(prefixExpression!=null) throw new RuntimeException(".super may only occur after a qualified name");
          ThisImpl newThis = factory().createThis();
          newThis.setThisClass(NameAnalysis.createUserTypeAttributeAsUserType(qname, currentScope));
          newThis.isSuper(true);
          prefixExpression = NameAnalysis.asAttribute(newThis);
          qname = null;
        }
        else
        {
          if (qname != null)
            qname = new QualifiedName((String)suffix, qname);
          else
            qname = QualifiedName.from((String)suffix);
        }
      } else if (suffix instanceof AArgumentsExpressionImpl) {
        // This is either an object allocation, anonymous allocation, or an array allocation.
        // If it's an array allocation, it is an error:
        if(suffix instanceof ArrayAllocation)
        {
          throw new RuntimeException("array allocation cannot be suffix.");
        }
        else if(suffix instanceof ObjectAllocationImpl)
        {
          Attribute<AExpression> pre = prefixExpression;
          if(pre==null) pre = NameAnalysis.createAccessExpressionAttribute(null, qname, currentScope);
          ((ObjectAllocationImpl)suffix).setPrefix(pre);
          ((ObjectAllocationImpl)suffix).setScope(currentScope);
          prefixExpression = NameAnalysis.asAttribute((ObjectAllocationImpl)suffix);
          qname = null;
        }
      } else if (suffix instanceof Attribute/*<AExpression>*/) {
        // this is an array index
        if (qname == null) {
          // the array is anonymous (return value or multi-dimensional)
          prefixExpression = NameAnalysis.asAttribute
            (factory().createArrayAccess (prefixExpression,
                                        (Attribute)suffix));
        } else {
          // access to named array: create an attribute for the array first...
          prefixExpression = NameAnalysis.createAccessExpressionAttribute
                               (prefixExpression,
                                qname,
                                currentScope);
          qname = null;
          // ...and now an attribute for the indexed access
          prefixExpression = NameAnalysis.asAttribute
            (factory().createArrayAccess (prefixExpression,
                                        (Attribute)suffix));
        }
      } else if (suffix instanceof List/*<Attribute<AExpression>>*/) {
        // this is a method call
        prefixExpression = NameAnalysis.createMethodCallExpressionAttribute
            (prefixExpression,
             qname,
             currentScope,
             (List)suffix);
        qname = null;
      } else {
        throw new RuntimeException("unknown suffix type");
      }
    }             
  )*
  { if (qname == null)
      return prefixExpression;
    else
      return NameAnalysis.createAccessExpressionAttribute (prefixExpression,
                                                           qname,
                                                           currentScope);
  }
}

java.lang.Object PrimaryPrefix() :
{ java.lang.Object result;
  Token t;
  Attribute<barat.reflect.AType> type;
}
{
  result = Literal()
  { return NameAnalysis.asAttribute ((LiteralImpl)result); }
|
  "this"
  { final ThisImpl ths = factory().createThis();
    ths.line_number(token.beginLine); 
    ths.setThisClass (NameAnalysis.createThisClassAttribute (currentScope, false));
    return NameAnalysis.asAttribute(ths);
  }
|
  "super" { /* was originally: "." t = <IDENTIFIER> (Boris) */ }
  { final ThisImpl spr = factory().createThis();
    spr.line_number(token.beginLine); 
    spr.setThisClass (NameAnalysis.createThisClassAttribute (currentScope, false));
    spr.isSuper(true);
    return NameAnalysis.asAttribute(spr);
  }
|
  "(" result = Expression() ")"
  { return NameAnalysis.asAttribute 
      (factory().createParenExpression ((Attribute)result)); 
  }
|
  LOOKAHEAD(1, <NEW>)
  result = AllocationExpression()
  { return NameAnalysis.asAttribute ((AExpression)result); }
|
  LOOKAHEAD( { isClassSelector() } )
  type=ResultType(new Token[1]) "." "class"
  { return NameAnalysis.asAttribute(factory().createClassExpression(type)); }
|
  result = Name(new Token[1])
  { return result; }
}

java.lang.Object PrimarySuffix() :
{ java.lang.Object result;
  Token t; 
}
{
  LOOKAHEAD( { getToken(1).kind == DOT && getToken(2).kind == THIS } )
  "." "this"
  { return "this"; /* this (sic!) is handled in PrimaryExpression() */ }
|
  LOOKAHEAD( { getToken(1).kind == DOT && getToken(2).kind == NEW } )
  "." result = AllocationExpression()
  { return result; }
|
  "[" result = Expression() "]"
  { return result; }
|
  "." t = <IDENTIFIER>
  { return t.image; }
|
  result = Arguments()
  { return result; }
}

LiteralImpl Literal() :
{ Token t; 
  LiteralImpl result; 
}
{
  t = <INTEGER_LITERAL>        
  { { String data = t.image;
    if (   data.charAt (data.length()-1) == 'l' 
        || data.charAt (data.length()-1) == 'L') {
      long longInt;
      if (data.startsWith ("0x") || data.startsWith ("0X"))
        longInt = parseLong (data.substring (2, data.length()-1), 16);
      else if (data.startsWith ("0") && data.length() > 1)
        longInt = parseLong (data.substring (1, data.length()-1), 8);
      else
        longInt = parseLong (data.substring (0, data.length()-1), 10);
      result = factory().createLiteral (new Long (longInt));
      result.line_number(token.beginLine); 
      return result;
    } else {
      int i;
      if (data.startsWith ("0x") || data.startsWith ("0X"))
        i = parseInt (data.substring (2, data.length()), 16);
      else if (data.startsWith ("0") && data.length() > 1)
        i = parseInt (data.substring (1, data.length()), 8);
      else
        i = parseInt (data.substring (0, data.length()), 10);
      result = factory().createLiteral (new Integer (i));
      result.line_number(token.beginLine); 
      return result;
    }
  } }
|
  t = <FLOATING_POINT_LITERAL>
  { {
      if(t.image.charAt (t.image.length()-1) == 'f'
      || t.image.charAt (t.image.length()-1) == 'F')
        result= factory().createLiteral (Float.valueOf (t.image));
      else
        // By default floating point literals are always double
        result = factory().createLiteral(Double.valueOf(t.image));
      result.line_number(token.beginLine); 
      return result;
  } } 
  |
  t = <CHARACTER_LITERAL>      { result = factory().createLiteral
                                   (new Character(parseChar(t.image.substring(1,t.image.length()-1))));
                                 result.line_number(token.beginLine); 
                                 return result;
                               }
|
  t = <STRING_LITERAL>         { result = factory().createLiteral(
	 			   parseString(
                                   (t.image.substring(1,t.image.length()-1))));
                                 result.line_number(token.beginLine); 
                                 return result;
                               }
|
  result = BooleanLiteral()    { return result; }
|
  result = NullLiteral()       { return result; }
}

LiteralImpl BooleanLiteral() :
{ LiteralImpl result; }
{
  "true"   { result = factory().createLiteral (Boolean.TRUE); result.line_number(token.beginLine); return result; }
|
  "false"  { result = factory().createLiteral (Boolean.FALSE); result.line_number(token.beginLine); return result; }
}

LiteralImpl NullLiteral() :
{ LiteralImpl result; }
{
  "null"   { result = factory().createLiteral (null); result.line_number(token.beginLine); return result; }
}

List<Attribute<AExpression>> Arguments() :
{ List<Attribute<AExpression>> result = null;
}
{
  "("
    [
      LOOKAHEAD(1, <BOOLEAN> | <BYTE> | <CHAR> | <DOUBLE> | <FALSE> | <FLOAT> |
                   <INT> | <LONG> | <NEW> | <NULL> | <SHORT> | <SUPER> | <THIS> |
                   <TRUE> | <VOID> | <INTEGER_LITERAL> | <FLOATING_POINT_LITERAL> |
                   <CHARACTER_LITERAL> | <STRING_LITERAL> | <IDENTIFIER> | <LPAREN> |
                   <BANG> | <TILDE> | <INCR> | <DECR> | <PLUS> | <MINUS>)
      result = ArgumentList()
    ]
  ")"
  { if (result == null)
      // When there are no arguments, return an empty list.
      // (We cannot return null, otherwise the kind of primary suffix
      // could not be recognized by our caller.)
      return new ArrayList<Attribute<AExpression>>();
    else
      return result;
  }
}

List<Attribute<AExpression>> ArgumentList() :
{ List<Attribute<AExpression>> result;
  Attribute<AExpression>       item;
}
{
  {
    result = new ArrayList<Attribute<AExpression>>();
    token_source.rememberTagsForExpression(currentScope);
  }
  item = Expression() 
  {
    result.add(item);
    token_source.setTagsFor(item, token_source.getTagsForExpression());
  }

  ( "," {
    token_source.rememberTagsForExpression(currentScope);
  }
    item = Expression()
  {
    result.add (item);
    token_source.setTagsFor(item, token_source.getTagsForExpression());
  } 
  )*
  { return result; }
}

AArgumentsExpressionImpl AllocationExpression() :
{ PrimitiveTypeImpl    ptype;
  QualifiedName        name;
  ArrayAllocationImpl  arrayAllocation;
  List<Attribute<AExpression>>   arguments;
  Token t = null;
  ClassImpl c = null;
  Scope oldScope;

  List<String> stored_tags;
}
{
  LOOKAHEAD( { getToken(2).kind != IDENTIFIER } )
  t="new" ptype = PrimitiveType(new Token[1]) arrayAllocation = ArrayDimsAndInits()
  { arrayAllocation.setElementType (new Constant<AType> (ptype));
    token_source.setTagsFor(arrayAllocation, t.endLine, currentScope);
    arrayAllocation.line_number(t.beginLine);
    return arrayAllocation;
  }
|
  t="new" name = Name(new Token[1])
    (
      LOOKAHEAD(1, <LBRACKET>)
      arrayAllocation = ArrayDimsAndInits()
      { arrayAllocation.setElementType 
             (NameAnalysis.createUserTypeAttribute (name, currentScope));
        token_source.setTagsFor(arrayAllocation, t.endLine, currentScope);
        arrayAllocation.line_number(t.beginLine);
        return arrayAllocation;
      }
    | {
	 stored_tags = token_source.storedTags;
	 token_source.storedTags = new ArrayList<String>();
      }
      arguments = Arguments()
      [
        {
          Scope scope = currentScope;
          while(!(scope instanceof ClassImpl))
          {
            scope = scope.getEnclosingScope();
          }
          ClassImpl enclosingClass = (ClassImpl)scope;
          int anonymousIndex = enclosingClass.getNextAnonymousIndex();
          c = factory().createClass(enclosingClass.getName() + "$" + anonymousIndex, null);
          c.setDelegate_modifiers(factory().createModifiers());
          c.setEnclosingScope(currentScope);
          oldScope = currentScope;
          currentScope = c;
        }
        ClassBody(c) 
        {
          currentScope = oldScope;
          Attribute<Class> anonClass = NameAnalysis.createAnonymousClassAttribute(c, name, currentScope);
          AnonymousAllocationImpl alloc = factory().createAnonymousAllocation(arguments, anonClass);
          alloc.setCalledConstructor(NameAnalysis.createConstructorAttribute (
                                                    name, 
                                                    currentScope,
                                                    arguments, alloc));
	  token_source.setTagsFor(alloc, t.endLine, currentScope, stored_tags);
	  alloc.line_number(t.beginLine);
          return alloc;
        }
      ]
      { ObjectAllocationImpl objectAllocation = factory().createObjectAllocation(arguments);
        objectAllocation.setCalledConstructor(
           NameAnalysis.createConstructorAttribute (name, 
                                                    currentScope,
                                                    arguments, objectAllocation));
        token_source.setTagsFor(objectAllocation, t.endLine, currentScope, stored_tags);
        objectAllocation.line_number(t.beginLine);
        return objectAllocation;
      }
    )
}

/*
 * The third LOOKAHEAD specification below is to parse to PrimarySuffix
 * if there is an expression between the "[...]".
 */
ArrayAllocationImpl ArrayDimsAndInits() :
{ ArrayAllocationImpl         result;
  Attribute<AExpression>      expression;
  Attribute<AExpression>      initializer; 
  int                         freeDimensions;
}
{
  LOOKAHEAD( { getToken(2).kind != RBRACKET } )

  { result         = factory().createArrayAllocation(); 
    freeDimensions = 0;
  }

  ( LOOKAHEAD( { getToken(1).kind == LBRACKET && getToken(2).kind != RBRACKET } )

    "[" expression = Expression() "]"

    { result.addArgument (expression); }
  )+

  ( LOOKAHEAD( { getToken(1).kind == LBRACKET && getToken(2).kind == RBRACKET } )
    "[" "]"
    { freeDimensions++; }
  )*
  { result.freeDimensions (freeDimensions); 
    return result;
  }

|
  { result         = factory().createArrayAllocation();
    freeDimensions = 0;
  }

  ( "[" "]" 
    { freeDimensions++; }
  )+ 
  initializer = ArrayInitializer()

  { result.freeDimensions (freeDimensions); 
    result.setInitializer 
      (new CastingAttribute<AExpression,ArrayInitializer> (initializer));
    return result;
  }
}


/*
 * Statement syntax follows.
 */

AStatementImpl Statement () :
{ AStatementImpl result; 
  Attribute<AExpression> e;
  Token startToken = null;  }
{
  {startToken=getToken(1);}
  (   LOOKAHEAD( { getToken(1).kind == IDENTIFIER && getToken(2).kind == COLON } )
      result = LabeledStatement()        { token_source.new_setTagsFor(result, startToken); return result; }
    |
      result = Block()                   { token_source.new_setTagsFor(result, startToken); return result; }
    |
      result = EmptyStatement()          { token_source.new_setTagsFor(result, startToken); return result; }
    |
      LOOKAHEAD(1, <BOOLEAN> | <BYTE> | <CHAR> | <DOUBLE> | <FALSE> | <FLOAT> |
        	   <INT> | <LONG> | <NEW> | <NULL> | <SHORT> | <SUPER> | <THIS> |
        	   <TRUE> | <VOID> | <INTEGER_LITERAL> | <FLOATING_POINT_LITERAL> |
        	   <CHARACTER_LITERAL> | <STRING_LITERAL> | <IDENTIFIER> |
        	   <LPAREN> | <INCR> | <DECR>)
      e      = StatementExpression() ";" 
      { result =  factory().createExpressionStatement (e); result.line_number(token.beginLine); token_source.new_setTagsFor(result, startToken); return result; }
    |
      result = SwitchStatement()         { token_source.new_setTagsFor(result, startToken); return result; }
    |
      result = IfStatement()             { token_source.new_setTagsFor(result, startToken); return result; }
    |
      result = WhileStatement()          { token_source.new_setTagsFor(result, startToken); return result; }
    |
      result = DoStatement()             { token_source.new_setTagsFor(result, startToken); return result; }
    |
      result = ForStatement()            { token_source.new_setTagsFor(result, startToken); return result; }
    |
      result = BreakStatement()          { token_source.new_setTagsFor(result, startToken); return result; }
    |
      result = ContinueStatement()       { token_source.new_setTagsFor(result, startToken); return result; }
    |
      result = ReturnStatement()         { token_source.new_setTagsFor(result, startToken); return result; }
    |
      result = ThrowStatement()          { token_source.new_setTagsFor(result, startToken); return result; }
    |
      result = SynchronizedStatement()   { token_source.new_setTagsFor(result, startToken); return result; }
    |
      result = TryStatement()            { token_source.new_setTagsFor(result, startToken); return result; }
  )
}

AStatementImpl LabeledStatement () :
{ Token t;
  AStatementImpl s; 
}
{
  t = <IDENTIFIER> ":" s = Statement()
  { ((ATargetStatementImpl)s).label (t.image); 
    s.line_number(t.beginLine);
    return s; 
  }
}

BlockImpl Block() :
{ BlockImpl block; 
  Scope enclosingScope = currentScope; }
{
  { block = factory().createBlock(); 
    block.setEnclosingScope(currentScope);
    currentScope=block;
  }
  "{"
  { block.line_number(token.beginLine); }
    (
      LOOKAHEAD(1, <BOOLEAN> | <BREAK> | <BYTE> | <CHAR> | <CLASS> | <CONTINUE> |
                   <DO> | <DOUBLE> | <FALSE> | <FINAL> | <FLOAT> | <FOR> | <IF> |
                   <INT> | <LONG> | <NEW> | <NULL> | <RETURN> | <SHORT> | <SUPER> |
                   <SWITCH> | <SYNCHRONIZED> | <THIS> | <THROW> | <TRUE> | <TRY> |
                   <VOID> | <WHILE> | <INTEGER_LITERAL> | <FLOATING_POINT_LITERAL> |
                   <CHARACTER_LITERAL> | <STRING_LITERAL> | <IDENTIFIER> |
                   <LPAREN> | <LBRACE> | <SEMICOLON> | <INCR> | <DECR>)
      BlockStatement (block)
    )*
  "}"
  { currentScope = enclosingScope;
    return block; 
  }
}

void BlockStatement (NodeImpl container) :
{ AStatementImpl s;
  Modifiers m;
}
{
  LOOKAHEAD( { isLocalVariableDeclaration() } )
  LocalVariableDeclaration (container) ";"
|
  LOOKAHEAD(1, <BOOLEAN> | <BREAK> | <BYTE> | <CHAR> | <CONTINUE> | <DO> |
               <DOUBLE> | <FALSE> | <FLOAT> | <FOR> | <IF> | <INT> | <LONG> |
               <NEW> | <NULL> | <RETURN> | <SHORT> | <SUPER> | <SWITCH> |
               <SYNCHRONIZED> | <THIS> | <THROW> | <TRUE> | <TRY> | <VOID> |
               <WHILE> | <INTEGER_LITERAL> | <FLOATING_POINT_LITERAL> |
               <CHARACTER_LITERAL> | <STRING_LITERAL> | <IDENTIFIER> | <LPAREN> |
               <LBRACE> | <SEMICOLON> | <INCR> | <DECR>)
  s = Statement ()
  { if (container instanceof BlockImpl)
      ((BlockImpl)container).addStatement (s); 
    else if (container instanceof ASwitchBranchImpl)
      ((ASwitchBranchImpl)container).addStatement (s);
  }
|
  LOOKAHEAD(1, <CLASS>)
  {
    UserTypeDeclarationImpl utd1 = factory().createUserTypeDeclaration();
    s = utd1;
    m = factory().createModifiers();
    utd1.setEnclosingScope(currentScope);
    currentScope = utd1;
  }
  UnmodifiedClassDeclaration(s, m, null)
  { if (container instanceof BlockImpl)
      ((BlockImpl)container).addStatement (s); 
    else if (container instanceof ASwitchBranchImpl)
      ((ASwitchBranchImpl)container).addStatement (s);
  }
|
  LOOKAHEAD(1, <INTERFACE>)
  {
    UserTypeDeclarationImpl utd2 = factory().createUserTypeDeclaration();
    s = utd2;
    m = factory().createModifiers();
    utd2.setEnclosingScope(currentScope);
    currentScope = utd2;
  }
  UnmodifiedInterfaceDeclaration(s, m, null)
  { if (container instanceof BlockImpl)
      ((BlockImpl)container).addStatement (s); 
    else if (container instanceof ASwitchBranchImpl)
      ((ASwitchBranchImpl)container).addStatement (s);
  }
}

void LocalVariableDeclaration (NodeImpl container) :
{ Attribute<AType>     type;
  Modifiers modifiers = new Modifiers();
  Token startToken = null;
  Token[] tHolder = new Token[1];
  NodeImpl item;
  LocalVariableImpl    lv;
  Token remember_me = null;
  VariableDeclarationImpl  vd; }
{
  { modifiers = factory().createModifiers(); }
  [ startToken="final" { modifiers.isFinal(true); } ] 
  type = Type(tHolder) 

  { if(startToken==null) startToken=tHolder[0];
    remember_me = token; }

  item = VariableDeclarator (type, true, startToken)
  { lv = (LocalVariableImpl)item;
    lv.setDelegate_modifiers (modifiers);
    vd = factory().createVariableDeclaration(lv);
    vd.line_number(remember_me.beginLine);
    vd.setEnclosingScope(currentScope);
    currentScope = vd;
    if (container instanceof BlockImpl)
      ((BlockImpl)container).addStatement (vd); 
    else if (container instanceof ASwitchBranchImpl)
      ((ASwitchBranchImpl)container).addStatement (vd);
    else if (container instanceof ForInitDeclarationImpl)
      ((ForInitDeclarationImpl)container).addDeclaration(vd);
  } 

  ( "," { remember_me = token; } item = VariableDeclarator (type, true, startToken) 
      { lv = (LocalVariableImpl)item;
        lv.setDelegate_modifiers (modifiers);
        vd = factory().createVariableDeclaration(lv);
        vd.line_number(remember_me.beginLine);
        vd.setEnclosingScope(currentScope);
        currentScope = vd;
        if (container instanceof BlockImpl)
          ((BlockImpl)container).addStatement (vd); 
        else if (container instanceof ASwitchBranchImpl)
          ((ASwitchBranchImpl)container).addStatement (vd);
        else if (container instanceof ForInitDeclarationImpl)
          ((ForInitDeclarationImpl)container).addDeclaration(vd);
      } 
  )*
}

EmptyStatementImpl EmptyStatement () :
{ EmptyStatementImpl result; }
{
  ";" { result = factory().createEmptyStatement(); result.line_number(token.beginLine); return result; }
}

Attribute<AExpression> StatementExpression() :
/*
 * The last expansion of this production accepts more than the legal
 * Java expansions for StatementExpression.  This expansion does not
 * use PostfixExpression for performance reasons.
 */
{ Attribute<AExpression> expression;
  String operator;
  Attribute<AExpression> rightOperand;
}
{
  expression = PreIncrementExpression()
  { return expression; }
|
  expression = PreDecrementExpression()
  { return expression; }
|
  LOOKAHEAD(1, <BOOLEAN> | <BYTE> | <CHAR> | <DOUBLE> | <FALSE> | <FLOAT> |
               <INT> | <LONG> | <NEW> | <NULL> | <SHORT> | <SUPER> | <THIS> |
               <TRUE> | <VOID> | <INTEGER_LITERAL> | <FLOATING_POINT_LITERAL> |
               <CHARACTER_LITERAL> | <STRING_LITERAL> | <IDENTIFIER> | <LPAREN>)
  expression = PrimaryExpression()
  [
    "++"
    { expression = NameAnalysis.asAttribute
                     (factory().createUnaryOperation (expression, "++", true));
    }  
  |
    "--"
    { expression = NameAnalysis.asAttribute
                     (factory().createUnaryOperation (expression, "--", true));
    }
  |
    operator = AssignmentOperator() rightOperand = Expression()
    { expression = NameAnalysis.asAttribute
        (factory().createAssignment (expression, operator, rightOperand));
    }
  ]
  { return expression; }  
}

SwitchImpl SwitchStatement() :
{ SwitchImpl sw;
  ASwitchBranchImpl branch; 
  Attribute<AExpression> e;
  Scope enclosingScope = currentScope;
}
{
  "switch"

  { sw = factory().createSwitch(); sw.line_number(token.beginLine); }

  "(" e = Expression() ")" "{"

  { sw.setExpression (e); }
    (
      branch = SwitchLabel()
      (
        LOOKAHEAD(1, <BOOLEAN> | <BREAK> | <BYTE> | <CHAR> | <CLASS> | <CONTINUE> |
                     <DO> | <DOUBLE> | <FALSE> | <FINAL> | <FLOAT> | <FOR> | <IF> |
                     <INT> | <LONG> | <NEW> | <NULL> | <RETURN> | <SHORT> | <SUPER> |
                     <SWITCH> | <SYNCHRONIZED> | <THIS> | <THROW> | <TRUE> | <TRY> |
                     <VOID> | <WHILE> | <INTEGER_LITERAL> | <FLOATING_POINT_LITERAL> |
                     <CHARACTER_LITERAL> | <STRING_LITERAL> | <IDENTIFIER> |
                     <LPAREN> | <LBRACE> | <SEMICOLON> | <INCR> | <DECR>)
        BlockStatement (branch)
      )*
      { sw.addBranche (branch); }
    )*
  "}"
  { currentScope = enclosingScope; 
    return sw; 
  }
}

ASwitchBranchImpl SwitchLabel() :
{ Attribute<AExpression> e; CaseBranchImpl result = null; }
{
  "case"
  
  { result = factory().createCaseBranch(); result.line_number(token.beginLine); }
  
  e = Expression() ":"
  {
    result.setConstantExpression (e);
    return result;
  }
|
  "default" ":"
  { ASwitchBranchImpl result2 = factory().createDefaultBranch(); result2.line_number(token.beginLine); return result2; }
}

IfImpl IfStatement() :
/*
 * The disambiguating algorithm of JavaCC automatically binds dangling
 * else's to the innermost if statement.  The LOOKAHEAD specification
 * is to tell JavaCC that we know what we are doing.
 */
{ Attribute<AExpression> expression;
  AStatementImpl  thenBranch;
  AStatementImpl  elseBranch = null;
  Token start = null;
  List<String> tags;

}
{
  start="if" "(" expression = Expression() ")"
  thenBranch = Statement()  {
    tags = token_source.storedTags;
    token_source.storedTags = new ArrayList<String>(); /* backup tags */
  }
  [ LOOKAHEAD(1) "else" elseBranch = Statement() ]

  { IfImpl result = factory().createIf (expression, thenBranch, elseBranch);

    token_source.setTagsFor(result, start.endLine, currentScope, tags);

    result.line_number(start.beginLine);
    return result;
  }
}

WhileImpl WhileStatement() :
{ Attribute<AExpression> expression;
  AStatementImpl  body;
  Token start = null;
}
{
  start="while" "(" expression = Expression() ")" body = Statement()
  
  { WhileImpl result = factory().createWhile (expression, body);
    result.line_number(start.beginLine);
    return result;
  }
}

DoImpl DoStatement() :
{ Attribute<AExpression> expression;
  AStatementImpl  body;
  Token start = null;
}
{
  start="do" body = Statement() "while" "(" expression = Expression() ")" ";"
  
  { DoImpl result = factory().createDo (body, expression);
    result.line_number(start.beginLine);
    return result;
  }
}

ForImpl ForStatement() :
{ ForImpl         result     = null;
  AForInitImpl    init       = null;
  Attribute<AExpression> expression = null;
  AStatementImpl  body;
  Scope enclosingScope = currentScope;
}
{
  "for" "("
    { result = factory().createFor();
      result.line_number(token.beginLine);
    }
    [
      LOOKAHEAD(1, <BOOLEAN> | <BYTE> | <CHAR> | <DOUBLE> | <FALSE> | <FINAL> |
                   <FLOAT> | <INT> | <LONG> | <NEW> | <NULL> | <SHORT> | <SUPER> |
                   <THIS> | <TRUE> | <VOID> | <INTEGER_LITERAL> |
                   <FLOATING_POINT_LITERAL> | <CHARACTER_LITERAL> | <STRING_LITERAL> |
                   <IDENTIFIER> | <LPAREN> | <INCR> | <DECR>)
      ForInit (result)
    ]
    ";"
    [
      LOOKAHEAD(1, <BOOLEAN> | <BYTE> | <CHAR> | <DOUBLE> | <FALSE> | <FLOAT> |
                   <INT> | <LONG> | <NEW> | <NULL> | <SHORT> | <SUPER> | <THIS> |
                   <TRUE> | <VOID> | <INTEGER_LITERAL> | <FLOATING_POINT_LITERAL> |
                   <CHARACTER_LITERAL> | <STRING_LITERAL> | <IDENTIFIER> | <LPAREN> |
                   <BANG> | <TILDE> | <INCR> | <DECR> | <PLUS> | <MINUS>)
      expression = Expression()
      { result.setExpression (expression); }
    ]
    ";"
    [
      LOOKAHEAD(1, <BOOLEAN> | <BYTE> | <CHAR> | <DOUBLE> | <FALSE> | <FLOAT> |
                   <INT> | <LONG> | <NEW> | <NULL> | <SHORT> | <SUPER> | <THIS> |
                   <TRUE> | <VOID> | <INTEGER_LITERAL> | <FLOATING_POINT_LITERAL> |
                   <CHARACTER_LITERAL> | <STRING_LITERAL> | <IDENTIFIER> | <LPAREN> |
                   <INCR> | <DECR>)
      ForUpdate (result)
    ]
    ")"
    body = Statement()
    { result.setBody (body);
      currentScope = enclosingScope;
      return result; }
}

void ForInit (ForImpl container) :
{ AForInitImpl init; }
{
  LOOKAHEAD( { isLocalVariableDeclaration() } )
  { init = factory().createForInitDeclaration(); }
  LocalVariableDeclaration (init)
  { container.setForInit (init); }
|
  LOOKAHEAD(1, <BOOLEAN> | <BYTE> | <CHAR> | <DOUBLE> | <FALSE> | <FLOAT> |
               <INT> | <LONG> | <NEW> | <NULL> | <SHORT> | <SUPER> | <THIS> |
               <TRUE> | <VOID> | <INTEGER_LITERAL> | <FLOATING_POINT_LITERAL> |
               <CHARACTER_LITERAL> | <STRING_LITERAL> | <IDENTIFIER> | <LPAREN> |
               <INCR> | <DECR>)
  { init = factory().createForInitExpression(); init.line_number(token.beginLine); }
  StatementExpressionList (init)
  { container.setForInit (init); }
}

void StatementExpressionList (NodeImpl container) :
{ Attribute<AExpression> e; }
{
  e = StatementExpression()
  { if (container instanceof ForInitExpressionImpl) 
      ((ForInitExpressionImpl)container).addExpression (e);
    else if (container instanceof ForImpl)
      ((ForImpl)container).addUpdateExpression (e);
  }
  ( "," e = StatementExpression() 
        { if (container instanceof ForInitExpressionImpl) 
            ((ForInitExpressionImpl)container).addExpression (e);
          else if (container instanceof ForImpl)
            ((ForImpl)container).addUpdateExpression (e);
        }
  )*
}

void ForUpdate (ForImpl container) :
{}
{
  StatementExpressionList (container)
}

BreakImpl BreakStatement() :
{ Token t;
  String label = null;
  BreakImpl result;
}
{
  "break" 
  { result = factory().createBreak(); result.line_number(token.beginLine); }
  [ t = <IDENTIFIER> 
    { label = t.image; }
  ] ";"
  { result.setTarget (NameAnalysis.createBreakTargetAttribute(result, label)); 
    return result; }
}

ContinueImpl ContinueStatement() :
{ Token t;
  String label = null;
  ContinueImpl result;
}
{
  "continue"
  { result = factory().createContinue(); result.line_number(token.beginLine); }

  [ t = <IDENTIFIER> 
    { label = t.image; }
  ] ";"
  { result.setTarget (NameAnalysis.createContinueTargetAttribute(result, label)); 
    return result; }
}

ReturnImpl ReturnStatement() :
{ ReturnImpl      result;
  Attribute<AExpression> expression = null;
  Token t = null;
}
{
  t = "return"
  { result = factory().createReturn(); result.line_number(token.beginLine);
    token_source.rememberTagsForExpression(currentScope);
  }

  [
    LOOKAHEAD(1, <BOOLEAN> | <BYTE> | <CHAR> | <DOUBLE> | <FALSE> | <FLOAT> |
                 <INT> | <LONG> | <NEW> | <NULL> | <SHORT> | <SUPER> | <THIS> |
                 <TRUE> | <VOID> | <INTEGER_LITERAL> | <FLOATING_POINT_LITERAL> |
                 <CHARACTER_LITERAL> | <STRING_LITERAL> | <IDENTIFIER> | <LPAREN> |
                 <BANG> | <TILDE> | <INCR> | <DECR> | <PLUS> | <MINUS>)
    expression = Expression()
    {
      result.setExpression (expression);
      token_source.setTagsFor(expression, token_source.getTagsForExpression());
    }
  ]
  ";"
  { return result; }
}

ThrowImpl ThrowStatement() :
{ Attribute<AExpression> expression;
  Token start = null;
}
{
  start="throw" expression = Expression() ";"
  { ThrowImpl result = factory().createThrow (expression);
    result.line_number(start.beginLine);
    return result;
  }
}

SynchronizedImpl SynchronizedStatement() :
{ Attribute<AExpression> expression;
  BlockImpl       block;
  Token start = null;
}
{
  start="synchronized" "(" expression = Expression() ")" block = Block()
  { SynchronizedImpl result = factory().createSynchronized (expression, block);
    result.line_number(start.beginLine);
    return result;
  }
}

TryImpl TryStatement() :
/*
 * Semantic check required here to make sure that at least one
 * finally/catch is present.
 */
{ TryImpl         result;
  ParameterImpl   parameter;
  CatchImpl       catchClause;
  BlockImpl       block;
  Scope           enclosingScope = currentScope;
  Token start=null;
}
{
  "try"

  { result = factory().createTry(); result.line_number(token.beginLine); }

  block = Block()

  { result.setBlock (block); }

  ( "catch"
    {
      catchClause = factory().createCatch();
      catchClause.line_number(token.beginLine);
    }
    "(" parameter = FormalParameter() ")" 
    {
      catchClause.setParameter (parameter);
      catchClause.setEnclosingScope (currentScope);
      currentScope = catchClause;
    }
    block = Block()
    { catchClause.setBlock (block);
      result.addCatchClause (catchClause);
      currentScope = enclosingScope;
    }
  )*

  [ start="finally" block = Block() 
    {
      FinallyImpl fi = factory().createFinally (block);
      fi.line_number(start.beginLine);
      result.setFinallyClause (fi);
    }
  ]

  { return result; }
}
