header
{
/*
 *
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

package org.apache.royale.compiler.internal.parsing.as;

/*
 * This file is generated from ASTreeAssembler.g
 * DO NOT MAKE EDITS DIRECTLY TO THIS FILE.  THEY WILL BE LOST WHEN THE FILE IS GENERATED AGAIN!!!
 */

import java.util.ArrayList;
import java.util.List;

import org.apache.royale.compiler.tree.as.*;
import org.apache.royale.compiler.workspaces.IWorkspace;
import org.apache.royale.compiler.parsing.IASToken;
import org.apache.royale.compiler.parsing.IASToken.ASTokenKind;
import org.apache.royale.compiler.tree.as.IContainerNode.ContainerType;
import org.apache.royale.compiler.tree.as.ILiteralNode.LiteralType;
import org.apache.royale.compiler.internal.tree.as.*;
import org.apache.royale.compiler.internal.tree.as.metadata.*;
import org.apache.royale.compiler.asdoc.IASParserASDocDelegate;
import org.apache.royale.compiler.constants.IASLanguageConstants;
import org.apache.royale.compiler.problems.ICompilerProblem;
import org.apache.royale.compiler.problems.*;

}

/**
 * ActionScript3 parser grammar. It consumes ASTokens and produces IASNode AST.
 * The number of tokens in a single syntactic predicate can not be greater than
 * StreamingTokenBuffer.REWIND_BUFFER_SIZE.
 *
 * @see <a href="https://zerowing.corp.adobe.com/display/FlashPlayer/ActionScript+Language+Specification">ActionScript Language Syntax Specification</a>
 */
class ASParser extends Parser("org.apache.royale.compiler.internal.parsing.as.BaseASParser");
 
options
{ 
	exportVocab = AS;
	defaultErrorHandler = false;
}	

tokens
{	
	// These hidden tokens are matched by the raw tokenizer but are not sent to the parser.
	HIDDEN_TOKEN_COMMENT; 
	HIDDEN_TOKEN_SINGLE_LINE_COMMENT; 
	HIDDEN_TOKEN_STAR_ASSIGNMENT; 
	HIDDEN_TOKEN_BUILTIN_NS;
	HIDDEN_TOKEN_MULTI_LINE_COMMENT; 
	
	// These two tokens are used by code model's ASDoc tokenizer.
	TOKEN_ASDOC_TAG; 
	TOKEN_ASDOC_TEXT; 
	
	// These tokens are transformed from reserved words by StreamingASTokenizer.
	TOKEN_RESERVED_WORD_EACH; 
	TOKEN_RESERVED_WORD_CONFIG;
	TOKEN_KEYWORD_INCLUDE;
	TOKEN_RESERVED_WORD_GOTO; 
}

{

    /**
     * Construct an AS3 parser from a token buffer.
     */
    public ASParser(IWorkspace workspace, IRepairingTokenBuffer buffer) 
    {
    	super(workspace, buffer);
    	tokenNames = _tokenNames;
    }

	/**
     * Construct an AS3 parser for parsing command line config args
     */
	public ASParser(IWorkspace workspace, IRepairingTokenBuffer buffer, boolean parsingProjectConfigVariables) 
    {
    	super(workspace, buffer, parsingProjectConfigVariables);
    	tokenNames = _tokenNames;
    }
}


/**
 * Matches multiple directives. This layer is added to handle parsing error in directives.
 */
fileLevelDirectives[ContainerNode c]
    :   (directive[c, NO_END_TOKEN])*
    ;
    exception catch [RecognitionException parserError] { handleParsingError(parserError);  }

/**
 * Matches a "directive" level input.
 * The first couple of alternatives gated with semantic predicates are used to
 * either disambiguate inputs, or to trap erroneous syntax.
 */
directive[ContainerNode c, int endToken]
{
    final ASToken lt1 = LT(1);
    final ASToken lt2 = LT(2);
    final int la1 = LA(1);
    final int la2 = LA(2);
    final int la3 = LA(3);
    final int la4 = LA(4);
}
    :   { la1 == TOKEN_BLOCK_OPEN }? groupDirective[c, endToken]
    |   { la1 == TOKEN_RESERVED_WORD_NAMESPACE && la2 == TOKEN_PAREN_OPEN }? statement[c, endToken] 
    |   { la1 == TOKEN_IDENTIFIER && la2 == TOKEN_NAMESPACE_ANNOTATION && lt1.getLine() == lt2.getLine() }?
        // Skip over the user-defined namespace name and continue.
        nsT:TOKEN_IDENTIFIER attributedDefinition[c]
        { trapInvalidNamespaceAttribute((ASToken)nsT); }
    |   asDocComment
    |   importDirective[c]
    |   useNamespaceDirective[c] 
    |   { la1 == TOKEN_NAMESPACE_NAME && 
    	  la2 == TOKEN_OPERATOR_NS_QUALIFIER && 
    	  la3 == TOKEN_IDENTIFIER && 
    	  la4 == TOKEN_BLOCK_OPEN}?
        // ns::var { ... }
        groupDirectiveWithConfigVariable[c, endToken]
    |   { la1 == TOKEN_NAMESPACE_NAME &&
    	  la2 == TOKEN_OPERATOR_NS_QUALIFIER &&
    	  la3 == TOKEN_NAMESPACE_ANNOTATION }?
        // ns::var private var foo:int;
        attributedDefinition[c]  
    |   { la1 == TOKEN_NAMESPACE_NAME &&
    	  la2 == TOKEN_OPERATOR_NS_QUALIFIER }? 
        // "ns::var" or "ns::[x, y]"
        statement[c,endToken]
    |   { !isFunctionClosure() }? 
        attributedDefinition[c]  
    |   packageDirective[c]
    |   statement[c,endToken]
    |   configNamespace[c]  
    |   includeDirective
    // The following alternatives are error traps
    |   fT:TOKEN_KEYWORD_FINALLY   { reportUnexpectedTokenProblem((ASToken)fT); }
    |   cT:TOKEN_KEYWORD_CATCH     { reportUnexpectedTokenProblem((ASToken)cT); }
    ;
    exception catch [RecognitionException ex]
    { 
    	handleParsingError(ex, endToken);  
    	consumeUntilKeywordOrIdentifier(endToken); 
    }
	
/**
 * Include processing is usually done in the lexer. However, this rule is added
 * in order to support code model partitioner whose tokenizer is set to not
 * "follow includes". In a normal AS3 compilation, the parser would never see
 * the "include" token. 
 */
includeDirective
    :   TOKEN_KEYWORD_INCLUDE TOKEN_LITERAL_STRING 
    ;
    
/**
 * Matches an attributed definition. An "attribute" can be a namespace or a 
 * modifier.
 */
attributedDefinition[ContainerNode c]	
{ 
    List<INamespaceDecorationNode> namespaceAttributes = new ArrayList<INamespaceDecorationNode>();
    List<ModifierNode> modifiers = new ArrayList<ModifierNode>(); 
    INamespaceDecorationNode namespaceAttr = null;
    
    boolean enabled = isDefinitionEnabled(c);
    boolean eval = true;
}
    :   (eval=configConditionOfDefinition)?
        {
        	// A configuration condition variable can either be matched by
        	// the above rule or be transformed into a LiteralNode of boolean
        	// type. If either is evaluated to false, the definition is disabled.
        	enabled &= eval;
            if (!enabled)
			    c = new ContainerNode(); 
        }
        (attribute[modifiers, namespaceAttributes])* 
        {
            // Verify that at most one namespace attribute is matched.
            verifyNamespaceAttributes(namespaceAttributes);
            
            if (!namespaceAttributes.isEmpty())
               namespaceAttr = namespaceAttributes.get(0);
        }
        definition[c, namespaceAttr, modifiers]
    	exception catch [RecognitionException ex]
    	{ 
    		handleParsingError(ex);  
    	}
    ;

/**
 * Matches an attribute such as:
 * - Modifiers: dynamic, final, native, override, static, virtual.
 * - Namespace names.
 * - Reserved namespace names: internal, private, public, protected.
 *
 * A definition can have at most one "namespace attribute".
 * The matched attribute is added to the lists passed in as arguments.
 */
attribute [List<ModifierNode> modifiers, List<INamespaceDecorationNode> namespaceAttributes] 
{
    ExpressionNodeBase namespaceNode = null; 
    ModifierNode modifierNode = null;
}
    :   modifierNode=modifierAttribute
        {   
            if (modifierNode != null)
                modifiers.add(modifierNode);
        }
    |   namespaceNode=namespaceModifier 
        {
            if (namespaceNode instanceof INamespaceDecorationNode)
                namespaceAttributes.add((INamespaceDecorationNode) namespaceNode); 
        }
    ;
	
	
/**
 * Matches a definition of variable, function, namespace, class or interface.
 */
definition[ContainerNode c, INamespaceDecorationNode ns, List<ModifierNode> modList]
    :   variableDefinition[c, ns, modList]
    |   functionDefinition[c, ns, modList]
    |   namespaceDefinition[c, ns, modList]
    |   classDefinition[c, ns, modList]
    |   interfaceDefinition[c, ns, modList]
    ;
	
/**
 * Matches a "group" in a "group directive". 
 * Entering a "Block" leaves the global context, but entering a "Group" doesn't.
 */
groupDirective[ContainerNode c, int endToken]
{ 
    BlockNode b = new BlockNode(); 
    enterGroup();
}
    :   openT:TOKEN_BLOCK_OPEN      { b.startAfter(openT); }
        (directive[c, endToken])*
    	{ if(b.getChildCount() > 0) c.addItem(b); }
    	closeT:TOKEN_BLOCK_CLOSE    { b.endBefore(closeT); leaveGroup(); }
    ;

/**
 * Matches a config condition such as "CONFIG::debug". This rule only applies 
 * to blocks gated with configuration variable.
 *
 * @return Evaluated result of the configuration variable.
 */
configCondition returns [boolean result]
{
	result = false;
}
    :   ns:TOKEN_NAMESPACE_NAME op:TOKEN_OPERATOR_NS_QUALIFIER id:TOKEN_IDENTIFIER
        {
            result = evaluateConfigurationVariable(new NamespaceIdentifierNode((ASToken)ns), (ASToken) op, new IdentifierNode((ASToken)id));
        }
    ;
    
/**
 * Similar to "configCondition", only that the token type after "::" is 
 * "TOKEN_NAMESPACE_ANNOTATION". This rule only applies to "attributed 
 * definitions".
 */
configConditionOfDefinition returns [boolean result]
{
	result = false;
}
    :   ns:TOKEN_NAMESPACE_NAME op:TOKEN_OPERATOR_NS_QUALIFIER id:TOKEN_NAMESPACE_ANNOTATION
        {
            result = evaluateConfigurationVariable(new NamespaceIdentifierNode((ASToken)ns), (ASToken) op, new IdentifierNode((ASToken)id));
        }
    ;
    
/**
 * Matches a group of directives gated with configuration variable.
 *
 *     CONFIG::debug {
 *         trace("debugging code");
 *     }
 *
 * If the configuration variable evaluates to false, the following block will
 * not be added to the resulting AST.
 */
groupDirectiveWithConfigVariable [ContainerNode c, int endToken]
{
    boolean b;
    ConfigConditionBlockNode block;
    final Token lt = LT(1);
}
    :   b=configCondition   
        {
        	block = new ConfigConditionBlockNode(b);
        	block.startBefore(lt);
        	c.addItem(block);
        }
        groupDirective[block, endToken]
    ;
	
/**
 * Matches a statement.
 *
 * Note that the "SuperStatement" in ASL syntax spec is not explicitly defined.
 * The "super" statements like <code>super(args);</code> are matched as regular
 * "call" expressions.
 */
statement[ContainerNode c, int exitCondition]
{
    final int la1 = LA(1);
    final int la2 = LA(2);
}
    :   breakOrContinueStatement[c]
    |   defaultXMLNamespaceStatement[c]
    |   gotoStatement[c]                 
    |   emptyStatement
    |   { la1 == TOKEN_IDENTIFIER && la2 == TOKEN_COLON }? labeledStatement[c, exitCondition]
    |   { la1 != TOKEN_SQUARE_OPEN && 
    	  la1 != TOKEN_OPERATOR_LESS_THAN && 
    	  la1 != TOKEN_BLOCK_OPEN }? expressionStatement[c]
    |   forStatement[c]
    |   ifStatement[c]
    |   meta[c]
    |   returnStatement[c]
    |   switchStatement[c]
    |   throwsStatement[c]
    |   tryStatement[c]
    |   whileStatement[c]
    |   doStatement[c]
    |   withStatement[c]
    ;
    exception catch [RecognitionException ex]
    { 
        handleParsingError(ex);  
        consumeUntilKeywordOrIdentifier(exitCondition); 
    }
    
/**
 * Matches an "expression statement". The ASL syntax specification says the 
 * lookahead can not be "[", "{" or "function". Legacy code requires that "<"
 * be excluded as well.
 */
expressionStatement[ContainerNode c]
{ 
    ExpressionNodeBase e = null; 
    
    if (LA(1) == TOKEN_KEYWORD_FUNCTION)
    {
        // Recover: continue parsing function as an anonymous function.
        logSyntaxError(LT(1));
    }
}
    :   e=expression
        {
            c.addItem(e);
            if (!matchOptionalSemicolon())
            {
                recoverFromExpressionStatementMissingSemicolon(e);
            }
        }
    ;

/**
 * <h1>From ASL syntax spec:</h1>
 * <quote>
 * InnerSubstatement is defined in the grammar for the sole purpose of 
 * specifying side conditions that disambiguate various syntactic ambiguities 
 * in a context-sensitive manner specified in Section 5.
 * </quote>
 *
 * It is only used in "do statement" and "if statement" to loosen the following
 * two cases allowed by AS3 but not by ECMA5.
 *
 * <code>
 * do x++ while (x < 10);     // ES5 would require a ; after x++
 * if (x > 10) x++ else y++;  // ES5 would require a ; after x++
 * <code>
 */
innerSubstatement[ContainerNode c]
    :   substatement[c] { afterInnerSubstatement(); }
    ;
    
/**
 * Matches a sub-statement. 
 */
substatement[ContainerNode c]
    :   (   { LA(1) != TOKEN_BLOCK_OPEN }? statement[c,NO_END_TOKEN]
        |   block[c] 
        |   variableDefinition[c, null, null]
        )
        {
        	if (c.getContainerType() == ContainerType.SYNTHESIZED)
            	c.setContainerType(ContainerType.IMPLICIT);
        }
    ;

/**
 * Matches a "labeled statement". For example:
 *
 *     innerLoop: x++;
 *
 */
labeledStatement[ContainerNode c, int exitCondition]
{
	LabeledStatementNode statementNode = null;
	ASToken offendingNSToken = null;
}
    :   labelT:TOKEN_IDENTIFIER TOKEN_COLON 
        { 
            final NonResolvingIdentifierNode labelNode = 
            	new NonResolvingIdentifierNode(
                    labelT != null ? labelT.getText() : "",
                    labelT);	
            statementNode = new LabeledStatementNode(labelNode);
            c.addItem(statementNode);
        }
        (   { LA(1) == TOKEN_RESERVED_WORD_NAMESPACE && LA(2) == TOKEN_IDENTIFIER }?
            { offendingNSToken = LT(1); }
            namespaceDefinition[c, null, null] 
            { trapInvalidSubstatement(offendingNSToken); }
        |   substatement[statementNode.getLabeledStatement()]
        )
    ;
		 	
/**
 * Matches a block.
 */
block[ContainerNode b]
    :   openT:TOKEN_BLOCK_OPEN      
        { 
        	b.startAfter(openT); 
            b.setContainerType(ContainerType.BRACES);
        }
        (directive[b, TOKEN_BLOCK_CLOSE])*
        closeT:TOKEN_BLOCK_CLOSE    { b.endBefore(closeT); }
    ;
    exception catch [RecognitionException ex]
    { 
        handleParsingError(ex);   
        consumeUntilKeywordOrIdentifier(TOKEN_BLOCK_CLOSE); 
        endContainerAtError(ex, b);
    }

/**
 * Matches an import directive.
 *
 *     import flash.display.Sprite;
 *     import flash.events.*;
 */
importDirective[ContainerNode c]
{  
	ExpressionNodeBase n = null; 
	ImportNode i = null; 
	IIdentifierNode alias = null;
}
    :   importT:TOKEN_KEYWORD_IMPORT 
    	{
    		i = new ImportNode((ExpressionNodeBase) null);
    		i.startBefore(importT);
    		i.endAfter(importT); 
    		c.addItem(i);
			if (LA(2) == TOKEN_OPERATOR_ASSIGNMENT)
			{
				alias = identifier();
				match(TOKEN_OPERATOR_ASSIGNMENT);
			}
    	}

        n=importName
    	{
     		if(n != null) {
				if(alias != null) {
					i.setImportAlias(alias.getName());
				}
     			i.setImportTarget(n);
     			i.setEnd(n.getEnd());
     			encounteredImport(i);
     		} 
     		else {
     			i.setImportTarget(new IdentifierNode(""));
     		}
     		matchOptionalSemicolon();
    	}
    ;
    exception catch [RecognitionException ex] { handleParsingError(ex); }

/**
 * Matches "use namespace ns" directive.
 */
useNamespaceDirective[ContainerNode c]
{  
	ExpressionNodeBase n = null;
	UseNamespaceNode u = null;
}
    :   useT:TOKEN_KEYWORD_USE { u = new UseNamespaceNode(n); u.startBefore(useT); c.addItem(u); }
        nsT:TOKEN_RESERVED_WORD_NAMESPACE { u.endAfter(nsT); } n=restrictedName
    	{
    		u.setTargetNamespace(n);
    		u.setEnd(n.getEnd());
    		matchOptionalSemicolon();
    	}
    ;
    exception catch [RecognitionException ex] 
	{ 
		if (u != null && u.getTargetNamespace() == null) 
			u.setTargetNamespace(handleMissingIdentifier(ex));
		else 
			handleParsingError(ex);  
	}

/**
 * Matches an ASDoc block.
 */		 
asDocComment 
    :   asdocT:TOKEN_ASDOC_COMMENT 
		{
			asDocDelegate.setCurrentASDocToken(asdocT);
		}
	;

/**
 * Matches a "modifier attribute" such as "final", "dynamic", "override", 
 * "static" or "native".
 */
modifierAttribute returns [ModifierNode modifierNode]
{ 
    modifierNode = null;
    final ASToken modifierT = LT(1); 
}
    :   (   TOKEN_MODIFIER_FINAL
        |   TOKEN_MODIFIER_DYNAMIC
        |   TOKEN_MODIFIER_OVERRIDE
        |   TOKEN_MODIFIER_STATIC
        |   TOKEN_MODIFIER_NATIVE
        |   TOKEN_MODIFIER_VIRTUAL
        )
        { modifierNode = new ModifierNode((ASToken) modifierT);	}
	;
	
 
/**
 * Matches a namespace modifier on an "attributed definition".
 */
namespaceModifier returns[ExpressionNodeBase n]
{ 
    n = null;
}
    :   nsPart1T:TOKEN_NAMESPACE_ANNOTATION
    	{ 
        	// If our text token is a member access, then build a normal 
        	// identifier. Otherwise, build a NS specific one.
        	
        	if (LA(1) == TOKEN_OPERATOR_MEMBER_ACCESS) 
        	{
                n = new IdentifierNode((ASToken)nsPart1T) ;
            }
            else
            {
                final NamespaceIdentifierNode nsNode = new NamespaceIdentifierNode((ASToken)nsPart1T); 
        		nsNode.setIsConfigNamespace(isConfigNamespace(nsNode));
        		n = nsNode;
        	}
    	}
    	(
            dotT:TOKEN_OPERATOR_MEMBER_ACCESS
            (
                nsNameT:TOKEN_NAMESPACE_ANNOTATION
        		{
        			IdentifierNode id = new IdentifierNode((ASToken)nsNameT);
        			n = new FullNameNode(n, (ASToken) dotT, id);
        		}
        	)
    	)*
    	{
            if (n instanceof FullNameNode) 
        	   n = new QualifiedNamespaceExpressionNode((FullNameNode)n);
    	}
    
    ;

/**
 * Matches a "metadata statement".
 *
 *     [ExcludeClass()]
 *     [Bindable]
 */
meta[ContainerNode c]
{
	ArrayLiteralNode al = new ArrayLiteralNode(); 
	final ASToken lt = LT(1);
}
    :   attributeT:TOKEN_ATTRIBUTE
        { 
        	// Note that a separate parser is invoked here for metadata.
            parseMetadata(attributeT, errors); 
            preCheckMetadata(attributeT, c);
    	} 
	|   { isIncompleteMetadataTagOnDefinition() }?
	    TOKEN_SQUARE_OPEN
	    // Error trap for "[" before a definition item.
	    { logSyntaxError(LT(1)); } 
    |   arrayInitializer[al]  
        // This is statement-level metadata.
        {
        	// Synthesize a MetaTagsNode to hold the metadata offsets.
            currentAttributes = new MetaTagsNode();
            currentAttributes.span(al, al);
            preCheckMetadata(lt, c);
        }
    ;
 	exception catch [RecognitionException ex] 
 	{ 
 		recoverFromMetadataTag(c, al); 
	}
 	
/**
 * Matches a "config namespace foo" directive.
 */
configNamespace[ContainerNode c]
	:   TOKEN_RESERVED_WORD_CONFIG TOKEN_RESERVED_WORD_NAMESPACE configN:TOKEN_IDENTIFIER 
		{
			NamespaceNode cNode = new ConfigNamespaceNode(new IdentifierNode((ASToken)configN));
			addConditionalCompilationNamespace(cNode);
            matchOptionalSemicolon();
		}
	    
	;
	exception catch [RecognitionException ex]
	{ handleParsingError(ex); }

/**
 * Matches a "package" block.
 *
 *     package mx.controls { ... }
 *
 */
packageDirective[ContainerNode c]
{ 
	PackageNode p = null; 
	ExpressionNodeBase name = null; 
	BlockNode b = null; 
}	
    :   packageT:TOKEN_KEYWORD_PACKAGE { enterPackage((ASToken)packageT); }
        (name=packageName)?
        {
        	p = new PackageNode(name != null ? name : IdentifierNode.createEmptyIdentifierNodeAfterToken(packageT), (ASToken)packageT);
        	p.startBefore(packageT);
        	c.addItem(p);
        	b = p.getScopedNode();
        }
        (   openT:TOKEN_BLOCK_OPEN { b.startAfter(openT); }
        	packageContents[b]
        )
        { leavePackage(); }
    ;
    exception catch [RecognitionException ex] { handleParsingError(ex); leavePackage();  }
	
/**
 * Matches a package name such as:
 *     org.apache.royale
 *
 * A Whitespace or LineTerminator is allowed around a . in a PackageName. 
 * For example, the following is a syntactically valid
 * <pre>
 * package a .
 *         b
 * { }
 * </pre>
 * The resulting PackageName value is equivalent to a PackageName without any intervening Whitespace and LineTerminators.
 */	
packageName returns [ExpressionNodeBase n]
{ 
	n = null; 
	ExpressionNodeBase e = null; 
}
 	:	n=identifier 
		(options{greedy=true;}: { LA(2) != TOKEN_OPERATOR_STAR }?
			dotT:TOKEN_OPERATOR_MEMBER_ACCESS 
			{ 
				n = new FullNameNode(n, (ASToken) dotT, null); 
			} 
			e=identifier
			{ 
				((FullNameNode)n).setRightOperandNode(e); 
			}
		)*
	;
	exception catch [RecognitionException ex] { return handleMissingIdentifier(ex, n); }	
	
/**
 * Matches contents in a package block.
 */
packageContents[ContainerNode b]
    :   (directive[b, TOKEN_BLOCK_CLOSE])* 
    	closeT:TOKEN_BLOCK_CLOSE { b.endBefore(closeT); }
    ;
    exception catch [RecognitionException ex]
	{
        if(handleParsingError(ex)) 
        {
        	//attempt to recover from the error so we can keep parsing within the block
            packageContents(b); 
        } 
        else 
        {
            endContainerAtError(ex, b); 
        } 
    }

/**
 * Matches a namespace definition.
 *
 *     namespace ns1;
 */
namespaceDefinition[ContainerNode c, INamespaceDecorationNode namespace, List<ModifierNode> modList]
{
	NamespaceNode n = null; 
	IdentifierNode id = null; 
	ExpressionNodeBase v = null; 
}
	:   nsT:TOKEN_RESERVED_WORD_NAMESPACE id=identifier
		{
			n = new NamespaceNode(id);
			n.startBefore(nsT);
			storeDecorations(n, c, namespace, modList);
            checkNamespaceDefinition(n);
		}
		(initializer[n])?
    	{ 
			c.addItem(n);
    		matchOptionalSemicolon(); 
    	}
	;
	exception catch [RecognitionException ex] { handleParsingError(ex); }	

/**
 * Matches an interface definition.
 *
 *     interface IFoo extends IBar {...}
 */
interfaceDefinition[ContainerNode c,  INamespaceDecorationNode namespace, List<ModifierNode> modList]
{ 	
	InterfaceNode interfaceNode = null; 
	IdentifierNode intName = null; 
	ExpressionNodeBase baseInterfaceName = null; 
	BlockNode b = null; 
    enterInterfaceDefinition(LT(1));
} 
    :   intT:TOKEN_KEYWORD_INTERFACE intName=identifier 
		{
			interfaceNode = new InterfaceNode(intName);
			interfaceNode.setInterfaceKeyword((IASToken)intT);
			storeDecorations(interfaceNode, c, namespace, modList);
			c.addItem(interfaceNode);
			
			// Recover from invalid interface name.
			final int la1 = LA(1);
			if (la1 != TOKEN_RESERVED_WORD_EXTENDS && la1 != TOKEN_BLOCK_OPEN)
			{
			    addProblem(new SyntaxProblem(LT(1)));
			    consumeUntilKeywordOr(TOKEN_BLOCK_OPEN);
			}
		}
		
		(	extendsT:TOKEN_RESERVED_WORD_EXTENDS
			{ interfaceNode.setExtendsKeyword((ASToken)extendsT); }
			(	baseInterfaceName=restrictedName
				{ 
					interfaceNode.addBaseInterface(baseInterfaceName);  
					interfaceNode.setEnd(baseInterfaceName.getEnd()); 
				}
 
				(	commaT:TOKEN_COMMA 
					{ interfaceNode.endAfter(commaT); }
					(	baseInterfaceName=restrictedName
						{ 
							interfaceNode.addBaseInterface(baseInterfaceName); 
							interfaceNode.setEnd(baseInterfaceName.getEnd()); 
						}
					)
				)*
			)
		)?
 
		{ 
 		  	b = interfaceNode.getScopedNode();
 		}
 	
 		openT:TOKEN_BLOCK_OPEN
			{ b.startAfter(openT); }
		classOrInterfaceBlock[b]
 	;
 	exception catch [RecognitionException ex] { handleParsingError(ex);  }
 
/**
 * Matches a class definition. For example:
 *
 *     class Player extends my_ns::GameObject implements IPlayer { ... }
 *
 */
classDefinition [ContainerNode c, INamespaceDecorationNode namespace, List<ModifierNode> modList]
{ 	
    IdentifierNode className = null; 
    ExpressionNodeBase superName = null; 
    ExpressionNodeBase interfaceName = null;
    ClassNode classNode = null;
    disableSemicolonInsertion();
    enterClassDefinition(LT(1));
}
    :   classT:TOKEN_KEYWORD_CLASS className=identifier
        {
            // When class name is empty, it is a synthesized IdentifierNode
            // created by the error recovery logic in "identifier" rule.
            // In such case, we fast-forward the token stream to the next
            // keyword to recover.
            if (className.getName().isEmpty())
            {
                // If the parser recover from "extends", "implements" or "{", 
                // we are could continue parsing the class definition, because
                // these tokens are the "follow set" of a class name token.
                // Otherwise, the next keyword is still a good starting point.
                consumeUntilKeywordOr(TOKEN_BLOCK_OPEN);
            }
            
            insideClass = true;
            classNode = new ClassNode(className);
            classNode.setSourcePath(((IASToken)classT).getSourcePath());
            classNode.setClassKeyword((IASToken)classT);
            storeDecorations(classNode, c, namespace, modList);
            c.addItem(classNode);
        }
         
        (   extendsT:TOKEN_RESERVED_WORD_EXTENDS 
            { classNode.setExtendsKeyword((ASToken)extendsT); }
            // The rule for super type should be "restrictedName". However, in
            // order to trap errors like "class Foo extends Vector.<T>", the
            // parser has to allow parameterized type as super name. It's up to
            // semantic analysis to report this problem.
            superName=type 
        	{ 
        		classNode.setBaseClass(superName); 
        		classNode.setEnd(superName.getEnd()); 
        	}
            exception catch [RecognitionException ex] { handleParsingError(ex); }
        )?
 
        (   impT: TOKEN_RESERVED_WORD_IMPLEMENTS 
            { classNode.setImplementsKeyword((ASToken)impT); }
            (   interfaceName=restrictedName
                { 
                    classNode.addInterface(interfaceName);
                    classNode.setEnd(interfaceName.getEnd());
                }
                (   commaT:TOKEN_COMMA 
                    { classNode.endAfter(commaT); }
                    interfaceName=restrictedName
                    { 
                        classNode.addInterface(interfaceName); 
                        classNode.setEnd(interfaceName.getEnd()); 
                    }
                )*
                exception catch [RecognitionException ex] { handleParsingError(ex); }
            )
            exception catch [RecognitionException ex] { handleParsingError(ex);  }
        )?
		
        openT:TOKEN_BLOCK_OPEN
        { classNode.getScopedNode().startAfter(openT); }
        classOrInterfaceBlock[classNode.getScopedNode()]
	;
	exception catch [RecognitionException ex] { handleParsingError(ex);  }

/**
 * Matches the content block of a class definition or an interface definition.
 */
classOrInterfaceBlock[BlockNode b]
 	{ enableSemicolonInsertion(); }
	
	:   (directive[b, TOKEN_BLOCK_CLOSE])*
		closeT:TOKEN_BLOCK_CLOSE
		{ b.endBefore(closeT); }
	;
	exception catch [RecognitionException ex] 
	{ 
		if(handleParsingError(ex))  {
			classOrInterfaceBlock(b); //attempt to retry
		} else {
			endContainerAtError(ex, b); 
		}
	}
	
/**
 * Matches an anonymous function (function closure).
 */
functionExpression returns [FunctionObjectNode n]
{ 
	n = null; 
	BlockNode b = null; 
	FunctionNode f = null; 
	IdentifierNode name=null; 
	ContainerNode p = null;
}
 	:   functionT:TOKEN_KEYWORD_FUNCTION 
 	
 		// optional function name
 		(name=identifier)?
		{
			if(name == null) 
				name = IdentifierNode.createEmptyIdentifierNodeAfterToken(functionT);
			f = new FunctionNode((ASToken)functionT, name);
			n = new FunctionObjectNode(f);
			f.startBefore(functionT);
			n.startBefore(functionT);
			b = f.getScopedNode();
			disableSemicolonInsertion();
		}
 
        // function signature
    	lpT:TOKEN_PAREN_OPEN
		{
			p = f.getParametersContainerNode();
			p.startBefore(lpT);
		}
    	formalParameters[p]
    	rpT:TOKEN_PAREN_CLOSE
 		{ p.endAfter(rpT); }
    	(resultType[f])?
     	{ enableSemicolonInsertion(); }	

		// non-optional function body     		
     	lbT:TOKEN_BLOCK_OPEN
		{ b.startAfter(lbT);}
     	functionBlock[f, (ASToken)lbT]		
 	;
 	exception catch [RecognitionException ex] { handleParsingError(ex);  }

/**
 * Matches a function block, excluding the open "{" but including the closing "}".
 */
functionBlock[FunctionNode f, ASToken openT]
{ 
    final BlockNode b = f.getScopedNode();
    b.setContainerType(IContainerNode.ContainerType.BRACES); 
    skipFunctionBody(f, openT);
}
	:   (directive[b, TOKEN_BLOCK_CLOSE])*
        rbT:TOKEN_BLOCK_CLOSE
	  	{ b.endBefore(rbT); }
	;
  	exception catch [RecognitionException ex] 
	{ 
		IASToken prev = buffer.previous();
        if (prev.getType() != ASTokenTypes.EOF)
            b.endAfter(prev);
        else
            b.setEnd(b.getStart());
		if(handleParsingError(ex))  {
			functionBlock(f, openT); //attempt to retry
		} 
	}

/**
 * Matches an optional function body. It can either be a "block" or a 
 * "semicolon".
 */
optionalFunctionBody [FunctionNode f]
{
    BlockNode blockNode = f.getScopedNode();
    enableSemicolonInsertion();
}
    :   { LA(1) == TOKEN_BLOCK_OPEN }? lbT:TOKEN_BLOCK_OPEN 
        { blockNode.startAfter(lbT); } 
        functionBlock[f, (ASToken)lbT]   
    |   { buffer.matchOptionalSemicolon() }? // Matches a function without body.
    |   {
            final Token lt = LT(1);
            blockNode.startBefore(lt);
            blockNode.endBefore(lt);

            // Report missing left-curly problem if there's no other syntax
            // problems in the function definition.
            reportFunctionBodyMissingLeftBraceProblem();
        } 
    ;
    exception catch [RecognitionException ex] { handleParsingError(ex); }

/**
 * Matches a function definition. For example:
 *
 *     private function myFunction(name:String) : void
 *     {
 *         return;
 *     }
 *
 */	
functionDefinition[ContainerNode c, INamespaceDecorationNode namespace, List<ModifierNode> modList]
{  
	IdentifierNode name=null; 
    disableSemicolonInsertion();
}
	:   (   functionT:TOKEN_KEYWORD_FUNCTION 
	
			// optional accessors:
			// Although "get" and "set" can be identifiers as well, here
			// they can only be the reserved words, unless it's a function
			// called "get()" or "set()". As a result, the parser
			// needs to match it in a "greedy" fashion.
	        (options{greedy=true;}:
	        	{ LA(2) != TOKEN_PAREN_OPEN}? getT:TOKEN_RESERVED_WORD_GET
        	|   { LA(2) != TOKEN_PAREN_OPEN}? setT:TOKEN_RESERVED_WORD_SET
        	)? 
        	
        	// non-optional function name:
	        name=identifier
			//we need to be able to keep going in case we are in the processing of typing a function name
			exception catch [RecognitionException ex] { name = handleMissingIdentifier(ex); }
		)
		{
			final FunctionNode n ;
			if (getT != null)
				n = new GetterNode((ASToken)functionT, (ASToken)getT, name);
			else if (setT != null)
				n = new SetterNode((ASToken)functionT, (ASToken)setT, name);
			else
				n = new FunctionNode((ASToken)functionT, name);
 
			storeDecorations(n, c, namespace, modList);
			c.addItem(n);
		}
		
		// function signature:
		lpT:TOKEN_PAREN_OPEN
		{
			final ContainerNode parameters = n.getParametersContainerNode();
			parameters.startBefore(lpT);
 		}
     	formalParameters[parameters]	
     	(	rpT:TOKEN_PAREN_CLOSE 
     		{ parameters.endAfter(rpT); }
     		// error recovery for typing in-progress function definitions
     		exception catch [RecognitionException ex] { handleParsingError(ex); }
     	)
     	(resultType[n])?
     	
     	optionalFunctionBody[n]
 	;
	exception catch [RecognitionException ex] { handleParsingError(ex); }

/**
 * Matches the parameters of a function definition signature (excluding the parenthesis).
 * 
 *     arg1:int, arg2:String
 */
formalParameters[ContainerNode c]
	: (formal[c] (TOKEN_COMMA formal[c])*)?
	;
	exception catch [RecognitionException ex] {handleParsingError(ex); }

/**
 * Matches a single parameter in a function definition.
 */
formal[ContainerNode c]
	{ ParameterNode p = null; }
	
	:(p=restParameter | p=parameter)
		{ if (p != null) c.addItem(p); }
	;

/**
 * Matches the "rest parameters" in a function definition.
 *
 *     ...args
 */
restParameter returns [ParameterNode p]
	{ p = null; }
	
	: e:TOKEN_ELLIPSIS p=parameter
		{
			if (p != null){
				// ??? following is an override on default type-specification
				// ??? and should be pulled soon as that gets resolved.
				if (p.getTypeNode() == null){
					p.span(e);
				}
				p.setIsRestParameter(true);
			}
		}
	;
	
/**
 * Matches a parameter in a function definition.
 */
parameter returns [ParameterNode p]
{ 
	p = null; 
	ASToken t = null;
	IdentifierNode name = null; 
}
	:   (   t=varOrConst 
    		{
    			// const allowed here, var is not...log error, keep going
    			if (t.getType() == TOKEN_KEYWORD_VAR) 
    				handleParsingError(new RecognitionException()); 
    		}
        )? 
	
        name=identifier
		{ 
			p = new ParameterNode(name); 
			if (t != null && t.getType() == TOKEN_KEYWORD_CONST)
				p.setKeyword(t);
		}
		
        (resultType[p])?
        (initializer[p])?	 
	;
	exception catch [RecognitionException ex] { handleParsingError(ex); }

/**
 * Matches keyword "var" or keyword "const".
 */
varOrConst returns[ASToken token]
{ 
	token = LT(1); 
}
	:   TOKEN_KEYWORD_VAR 
	|   TOKEN_KEYWORD_CONST
	;
	
/**
 * Matches a result type: either a "void" keyword or a restricted name.
 *
 *     :void
 *     :String
 *     :int
 *
 */
resultType [BaseTypedDefinitionNode result]
{ 
    ExpressionNodeBase t = null; 
}
    :   colon:TOKEN_COLON 
        (   ( t=voidLiteral | t=type )
        	exception catch [RecognitionException ex] 
            { t = handleMissingIdentifier(ex); }
        )    
        { 
            if(t.getStart() == -1) 
                t.startAfter(colon);
            	
            if (t.getEnd() == -1) 
                t.endAfter(colon);
            	
            result.endAfter(colon);
            result.setType((ASToken) colon, t); 
        }
    ;
 	
/**
 * Matches an initializer in a variable/constant definition.
 */
initializer [IInitializableDefinitionNode v]
{
	ExpressionNodeBase e = null; 
}
  	:   assignT:TOKEN_OPERATOR_ASSIGNMENT 
        e=assignmentRightValue
 		{ v.setAssignedValue((IASToken) assignT, e); }
 	;	
 	exception catch [RecognitionException ex] {handleParsingError(ex);  }
 	
/**
 * Matches a variable/constant definition.
 */
variableDefinition[ContainerNode c, INamespaceDecorationNode namespace, List<ModifierNode> modList]
{
	VariableNode v = null;
	ChainedVariableNode v2 = null;
	ASToken tok = null;
	asDocDelegate.beforeVariable();
}
 	
	:   tok=varOrConst v=singleVariable[(ASToken)tok, namespace]
		{
			asDocDelegate.afterVariable();	
			storeVariableDecorations(v, c, namespace, modList);
			if(v instanceof ConfigConstNode) {
				addConfigConstNode((ConfigConstNode)v);
			} else {
				c.addItem(v);
			}
		}
    	// don't allow chain after a config
    	(   {!(v instanceof ConfigConstNode)}? 
    	    TOKEN_COMMA v2=chainedVariable[c]
    		{
    		 	if(v2 != null)
                {
                    v.addChainedVariableNode(v2);
                    storeEmbedDecoration(v2, v.getMetaTags());
                }
    		}
    		exception catch [RecognitionException ex] { handleParsingError(ex); }
    	)*
    	{ matchOptionalSemicolon(); setAllowErrorsInContext(true); }
	;
	exception catch [RecognitionException ex] { handleParsingError(ex); setAllowErrorsInContext(true); }

/**
 * Matches a single variable/constant definition.
 */
singleVariable[IASToken keyword, INamespaceDecorationNode namespace] returns [VariableNode v]
{ 
	v = null; 
	IdentifierNode name = null; 
}
	:   name=identifier
		{ 
			if(namespaceIsConfigNamespace(namespace)) {
				v = new ConfigConstNode(name);
			} else {
				v = new VariableNode(name);
			}
			v.setKeyword(keyword);
			v.setIsConst(keyword.getType() == TOKEN_KEYWORD_CONST);
			if(name.getStart() == -1) {
				name.startAfter(keyword);
				name.endAfter(keyword);
			}
		}
		
		(resultType[v])? 
		(initializer[v])?
	;
	exception catch [RecognitionException ex] { handleParsingError(ex); }
	
/**
 * Matches a chained variable/constant definition.
 */     
chainedVariable[ContainerNode c] returns [ChainedVariableNode v]
{
	 v = null; 
	 IdentifierNode name = null; 
}
	:   name=identifier
		{ v = new ChainedVariableNode(name); }
		
		(resultType[v])?
		(initializer[v])?
	;
	exception catch [RecognitionException ex] { handleParsingError(ex); }
	
/**
 * Matches variable definitions in a for loop.
 */
variableDefExpression returns[NodeBase v]
{ 
	v = null; 
	ContainerNode c = null; 
	NodeBase v1 = null; 
	ASToken varT = null;
} 
	:   varT=varOrConst v=singleVariableDefExpression[varT, varT.getType() == TOKEN_KEYWORD_CONST]
		(   TOKEN_COMMA v1=singleVariableDefExpression[null, varT.getType() == TOKEN_KEYWORD_CONST]
			{
				if (c == null) {
					c = new ContainerNode();
					c.setStart(v.getStart());
					c.addItem(v);
					v = c;
				}
				c.addItem(v1);
				c.setEnd(v1.getEnd());
			}
		)*
	;
	exception catch [RecognitionException ex] { handleParsingError(ex);  }
		
/**
 * Matches a single variable definition in a for loop.
 */
singleVariableDefExpression[ASToken varToken, boolean isConst] returns [ExpressionNodeBase n]
{
	n = null;
	VariableNode variable = null;
	IdentifierNode varName = null; 
	ExpressionNodeBase value = null; 
}
	:   varName=identifier
		{ 
			variable = new VariableNode(varName);
			if(varToken != null)
				variable.setKeyword(varToken);
			variable.setIsConst(isConst);
			n = new VariableExpressionNode(variable);
		}
		
    	(resultType[variable])?
    		
    	(initializer[variable])?
	;

/**
 * Matches a default XML namespace statement. For example:
 * 
 *    default xml namespace = "domain";
 *
 */
defaultXMLNamespaceStatement[ContainerNode c]
	{ ExpressionNodeBase e = null; }
	
	: defT:TOKEN_DIRECTIVE_DEFAULT_XML TOKEN_OPERATOR_ASSIGNMENT e=assignmentExpression
		{ 
			DefaultXMLNamespaceNode n = new DefaultXMLNamespaceNode(new KeywordNode((IASToken)defT));
			c.addItem(n);
			n.setExpressionNode(e); 
			matchOptionalSemicolon();
		}
	;

/**
 * Matches an expression in a pair of parenthesis. It's usually used as a
 * condition expression in {@code if (...)}, {@code while (...)}, etc.
 *
 *     (....)
 */
statementParenExpression returns [ExpressionNodeBase e]
{
	e = null; 
}
	:   TOKEN_PAREN_OPEN     { disableSemicolonInsertion(); }
		e=expression 
	    TOKEN_PAREN_CLOSE    { enableSemicolonInsertion(); }
	;
	exception catch [RecognitionException ex] { handleParsingError(ex); enableSemicolonInsertion(); }
	
		
/**
 * Matches an empty statement which is an explicit semicolon.
 */
emptyStatement
 	:   TOKEN_SEMICOLON
 	;

/**
 * Matches a "return" statement.
 */
returnStatement[ContainerNode c]
{ 
	ExpressionNodeBase n = null; 
	ExpressionNodeBase e = null;
}
	:   returnT:TOKEN_KEYWORD_RETURN 
		{
			n = new ReturnNode((ASToken)returnT);
			c.addItem(n);
			afterRestrictedToken((ASToken)returnT);
		}
		
		e=optExpression
		{ 
			((ReturnNode)n).setStatementExpression(e);
		}
	   { matchOptionalSemicolon(); }
	;
	exception catch [RecognitionException ex] { handleParsingError(ex); }

/**
 * Matches a "throw" statement.
 */
throwsStatement[ContainerNode c]
{ 
	ExpressionNodeBase n = null; 
	ExpressionNodeBase e = null;
}
	:   throwT:TOKEN_KEYWORD_THROW
		{
			n = new ThrowNode((ASToken)throwT);
			c.addItem(n);
			afterRestrictedToken((ASToken)throwT);
		}
	
		(   e=expression
			{ 
				((ThrowNode)n).setStatementExpression(e); 
			}
			exception catch [RecognitionException ex] { handleParsingError(ex); }
		)
	   { matchOptionalSemicolon(); }
	;
	
/**
 * Matches a "for loop" statement.
 */
forStatement[ContainerNode c]
{ 
    ForLoopNode node = null; 
    ContainerNode forContainer = null; 
    BlockNode b = null; 
    NodeBase fi = null; 
    ExpressionNodeBase e = null; 
    BinaryOperatorNodeBase inNode = null;
}
    :   forKeyword:TOKEN_KEYWORD_FOR lparenT:TOKEN_PAREN_OPEN
        { 
            node = new ForLoopNode((ASToken)forKeyword); 
            c.addItem(node);
            forContainer = node.getConditionalsContainerNode();
            b = node.getContentsNode();
            forContainer.startAfter(lparenT); 
        }
        
        { 
            expressionMode = ExpressionMode.noIn;
        }
        fi=forInitializer
        {
            expressionMode = ExpressionMode.normal;
        }
        
        (   TOKEN_SEMICOLON           { forContainer.addItem(fi); }
            forCondition[forContainer] 
            TOKEN_SEMICOLON 
            forStep[forContainer]
        |   in:TOKEN_KEYWORD_IN 
            {
                final ExpressionNodeBase leftOfIn;
                if (fi instanceof ExpressionNodeBase)
                {
                    leftOfIn = (ExpressionNodeBase) fi;
                }
                else
                {
                    // for...in doesn't allow multiple variable definition in the initializer clause
                    addProblem(new InvalidForInInitializerProblem(node));
                    if (fi instanceof ContainerNode &&
                        fi.getChildCount() > 0 &&
                        ((ContainerNode)fi).getChild(0) instanceof ExpressionNodeBase)
                    {
                        // Recover by taking the first variable initializer and
                        // drop the rest.
                        leftOfIn = (ExpressionNodeBase)((ContainerNode)fi).getChild(0);
                    }
                    else
                    {
                        // No valid variable initializer found: recover by adding
                        // an empty identifier node.
                        leftOfIn = IdentifierNode.createEmptyIdentifierNodeAfterToken((ASToken)lparenT);
                    }
                }
                inNode = BinaryOperatorNodeBase.create((ASToken)in, leftOfIn, null);
                forContainer.addItem(inNode);
            }
            e=optExpression
            { inNode.setRightOperandNode(e); }
        )?  // Make optional for error handling.
        
        {
            if (forContainer.getChildCount() == 0 && fi != null)
                forContainer.addItem(fi);
        }
        
        (   rparenT:TOKEN_PAREN_CLOSE
            { 
                forContainer.endBefore(rparenT); 
            }
            exception catch [RecognitionException ex] { handleParsingError(ex); }
        )
        substatement[b] 
    ;
    exception catch [RecognitionException ex] { handleParsingError(ex); }

/**
 * Matches the "initializer" part in a for loop.
 */    
forInitializer returns [NodeBase n]
{ 
	n = null; 
}
    :   n=variableDefExpression              								
    |   n=optExpression
    ;
    exception catch [RecognitionException ex] {handleParsingError(ex);  }

/**
 * Matches the "condition" part in a for loop.
 */
forCondition[ContainerNode c]
{ 
	ExpressionNodeBase e = null; 
}
	:   e=optExpression
		{if (e != null) c.addItem(e);}
	;
 	exception catch [RecognitionException ex] {handleParsingError(ex);  }

/**
 * Matches the "step" part in a for loop.
 */
forStep[ContainerNode c]
{ 
	ExpressionNodeBase e = null; 
}
	:    e=optExpression	
		{if (e != null) c.addItem(e);}
	;
 	exception catch [RecognitionException ex] {handleParsingError(ex);  }
 
/**
 * Matches a "do...while" statement.
 */
doStatement[ContainerNode c] 
{ 
	DoWhileLoopNode n = null; 
    ExpressionNodeBase e = null; 
    BlockNode b = null; 
}
 	:   doT:TOKEN_KEYWORD_DO
 		{
 			n = new DoWhileLoopNode((ASToken)doT);
 			c.addItem(n);
 			b = n.getContentsNode();
 		}
 		
        innerSubstatement[b]
        
        TOKEN_KEYWORD_WHILE e=statementParenExpression
 	 	{ 
 	 		n.setConditionalExpression(e); 	
	        matchOptionalSemicolon(); 
        }
 	;
 	exception catch [RecognitionException ex] {handleParsingError(ex);  }
 	
/**
 * Matches a "while" loop statement.
 * 
 *     while (x > 1) { x--; }
 */
whileStatement[ContainerNode c] 
{ 
	WhileLoopNode n = null; 
	ExpressionNodeBase e = null; 
	BlockNode b = null; 
}
 	:   whileT:TOKEN_KEYWORD_WHILE e=statementParenExpression	
 		{
 			n = new WhileLoopNode((ASToken)whileT);
 			n.setConditionalExpression(e);
 			c.addItem(n); 
 			b = n.getContentsNode();
 		}
 	
 	    substatement[b]
 	;
 	exception catch [RecognitionException ex] {handleParsingError(ex);  }
 	
 	
/**
 * Matches a "break statement" or a "continue statement". For example:
 *
 *    break;
 *    break innerLoop;
 *    continue;
 *    continue outerLoop;
 *
 */
breakOrContinueStatement[ContainerNode c]
{ 
    IdentifierNode id = null; 
    IterationFlowNode n = null; 
    final ASToken t = LT(1);
}
    :   ( TOKEN_KEYWORD_CONTINUE | TOKEN_KEYWORD_BREAK ) 
    	{ 
    		n = new IterationFlowNode(t); 
    		c.addItem(n); 
    		afterRestrictedToken(t);
    	}
    	
    	// "greedy" mode is required to associate the following ID with the flow control.
    	(options{greedy=true;}: 
    	   id=identifier 
    	   { n.setLabel(id); }	
	    )?
    	{ matchOptionalSemicolon(); }
    ;
    exception catch [RecognitionException ex] {handleParsingError(ex); }
 	
/**
 * Matches a "goto" statement.
 */
gotoStatement[ContainerNode c]
{ 
	IdentifierNode id = null; 
	IterationFlowNode n = null; 
	final ASToken t = LT(1);
}
    :   TOKEN_RESERVED_WORD_GOTO id=identifier
        {
    		n = new IterationFlowNode(t); 
    		c.addItem(n);
    		n.setLabel(id);
    		matchOptionalSemicolon();
        }
    ;
 	exception catch [RecognitionException ex] { handleParsingError(ex); }
 	
/**
 * Matches a "with" statement.
 */
withStatement[ContainerNode c]
{
	WithNode n = null; 
	ExpressionNodeBase e = null; 
	BlockNode b = null; 
}
	:   withT:TOKEN_KEYWORD_WITH e=statementParenExpression
		{ 
			n = new WithNode((ASToken)withT); 
			n.setConditionalExpression(e);
			c.addItem(n);
			b = n.getContentsNode(); 
		}
    	substatement[b]
	;

/**
 * Matches a "try...catch...finally" statement.
 */
tryStatement[ContainerNode c] 
{ 
	TryNode n = null; 
	BlockNode b = null; 
}
 	:   tryT:TOKEN_KEYWORD_TRY
 		{
 			n = new TryNode((ASToken)tryT);
 			b = n.getContentsNode();
 			c.addItem(n);
 		}
 		
        block[b]
        
        (   options { greedy=true;}: 
            catchBlock[n]
        )*

        (   options { greedy=true;}: 
            finallyT:TOKEN_KEYWORD_FINALLY
            {
            	TerminalNode t = new TerminalNode((ASToken)finallyT);
            	n.addFinallyBlock(t);
            	b = t.getContentsNode();
            }
            block[b]
        )?
 	;  

/**
 * Matches the "catch" block in a "try" statement.
 */
catchBlock[TryNode tryNode]
{ 
	CatchNode n = null; 
	ParameterNode arg = null; 
	BlockNode b = null; 
}
	:   catchT:TOKEN_KEYWORD_CATCH TOKEN_PAREN_OPEN 
		{ disableSemicolonInsertion(); } 
	  
	    arg=catchBlockArgument
		{ 
			n = new CatchNode(arg); 
			tryNode.addCatchClause(n);
			b = n.getContentsNode();
			n.startBefore(catchT); 
		}
		
		(   rpT:TOKEN_PAREN_CLOSE  
			{ 
				enableSemicolonInsertion();
				n.endAfter(rpT);
			}
			
 			exception catch [RecognitionException ex]
 			{handleParsingError(ex);  enableSemicolonInsertion(); }
 		)
 		
	    block[b]
	;
	exception catch [RecognitionException ex] { handleParsingError(ex); }

/**
 * Matches the argument in the "try...catch(arg)" statement.
 */
catchBlockArgument returns [ParameterNode p]
{ 
	p = null; 
	IdentifierNode name = null; 
	ExpressionNodeBase t = null; 
}
	:   name=identifier
		{ p = new ParameterNode(name); }
		
		(   colonT:TOKEN_COLON
			{ p.setType((ASToken)colonT, null); }
			t=type
			{ p.setType((ASToken)colonT, t); }
		)?
	;	
	exception catch [RecognitionException ex] { handleParsingError(ex); }

/**
 * Matches an "if" statement.
 */
ifStatement[ContainerNode c]
{ 
	IfNode i = null; 
	ExpressionNodeBase cond = null; 
	ContainerNode b = null; 
	boolean hasElse = false; 
}
	:   ifT:TOKEN_KEYWORD_IF cond=statementParenExpression  
		{
			i = new IfNode((ASToken)ifT);
			ConditionalNode cNode = new ConditionalNode((ASToken)ifT);			
			cNode.setConditionalExpression(cond);
			b = cNode.getContentsNode();
			i.addBranch(cNode);
			c.addItem(i);
		}
		innerSubstatement[b]
	
		(options{greedy=true;}: 
            hasElse=elsePart[i]
			{ if (hasElse == true) return; }
		)*
	;
 	exception catch [RecognitionException ex] { handleParsingError(ex); }
 	
/**
 * Matches the optional "else" block of an "if" statement.
 *
 * @return true if there is an "else" block.
 */
elsePart[IfNode i] returns [boolean hasElse]
{ 
	hasElse = false; 
	ContainerNode b = null; 
	ExpressionNodeBase cond = null; 
	ConditionalNode elseIf = null; 
}
	
	:   elseT:TOKEN_KEYWORD_ELSE
		(options{greedy=true;}: 
            TOKEN_KEYWORD_IF cond=statementParenExpression
			{
		    	elseIf = new ConditionalNode((ASToken) elseT);
		    	elseIf.setConditionalExpression(cond);
		    	i.addBranch(elseIf);
				b = elseIf.getContentsNode();
			}
		)?
		
		{
			if (elseIf == null){
				hasElse = true;
				TerminalNode t = new TerminalNode((ASToken) elseT);
				i.addBranch(t);
				b = t.getContentsNode();
			}
		}
		substatement[b]
	;
	exception catch [RecognitionException ex] {handleParsingError(ex);  }
	
/**
 * Matches a "switch" statement.
 */
switchStatement[ContainerNode c]
{ 
	SwitchNode sw = null; 
	ExpressionNodeBase e = null; 
}
 	:   switchT:TOKEN_KEYWORD_SWITCH e=statementParenExpression
 		{
			sw = new SwitchNode((ASToken)switchT);			
			c.addItem(sw);
			if(e != null)
				sw.setConditionalExpression(e);
 		}
 
 	    cases[sw]
 	;

/**
 * Matches the "case" block in a "switch" statement.
 */
cases[SwitchNode sw]
{ 
	final ContainerNode b = sw.getContentsNode(); 
}
    :   openT:TOKEN_BLOCK_OPEN           { b.startBefore(openT); }
    	caseClauses[b]
    	closeT:TOKEN_BLOCK_CLOSE         { b.endAfter(closeT); }
	;

/**
 * Matches the "case" clauses in a "switch" statement.
 */
caseClauses[ContainerNode swb]
 	:    (caseClause[swb])*	
 	;

/**
 * Matches a single "case" clause in a "switch" statement.
 */ 	
caseClause[ContainerNode swb]
{ 
	ExpressionNodeBase e = null; 
	ContainerNode b = null; 
}
 	:   caseT:TOKEN_KEYWORD_CASE e=expression colon
 		{
 			ConditionalNode cond = new ConditionalNode((ASToken) caseT);
 			cond.setConditionalExpression(e);
 		 	swb.addItem(cond);
 		 	b = cond.getContentsNode(); 
 		}
 		caseStatementList[b]
 		
 	|   defaultT:TOKEN_KEYWORD_DEFAULT colon 	
 		{
 			TerminalNode t = new TerminalNode((ASToken)defaultT);
 			swb.addItem(t);
 			b = t.getContentsNode();
 		}
 		caseStatementList[b]
 	;
 	exception catch [RecognitionException ex] {handleParsingError(ex);  }

/**
 * Matches a colon token ":" or recover from a missing colon.
 */
colon
	:	TOKEN_COLON
	;
 	exception catch [RecognitionException ex] { addProblem(unexpectedTokenProblem(LT(1), ASTokenKind.COLON)); }

/**
 * Matches the statements in a "case" clause.
 */ 	 
caseStatementList[ContainerNode b]
 	:   (directive[b, TOKEN_BLOCK_CLOSE])*
 	;
 	exception catch [RecognitionException ex] {handleParsingError(ex);  }
 
/**
 * Matches an identifier token. An identifier can come from different token
 * types such as: 
 *
 *   - IDENTIFIER
 *   - namespace
 *   - get 
 *   - set
 *
 * This is because in AS3, these elements are not reserved keyword. However they
 * have special meaning in some syntactic contexts.
 * See "AS3 syntax spec - 3.5 Keywords and Punctuators" for details.
 */
identifier returns [IdentifierNode n]
{ 
    n = null;  
    final ASToken token = LT(1);
}
    :   (	TOKEN_IDENTIFIER 
    	|	TOKEN_RESERVED_WORD_NAMESPACE 
    	|	TOKEN_RESERVED_WORD_GET 
    	| 	TOKEN_RESERVED_WORD_SET 
    	)
		{ n = new IdentifierNode(token); }
    ;
    exception
    catch [NoViableAltException e1] { n = expectingIdentifier(e1); }
    catch [RecognitionException e2] { n = handleMissingIdentifier(e2); }
    
/**
 * Matches an "import-able" name.
 *
 *     flash.display.Sprite;
 *     flash.events.*;
 */    
importName returns [ExpressionNodeBase n]
{ 
	n=null; 
	ExpressionNodeBase e = null;
}
	
	: 	n=packageName 
		(	dot:TOKEN_OPERATOR_MEMBER_ACCESS 
			{ 
				n = new FullNameNode(n, (ASToken) dot, null); 
			} 
			e=starLiteral
			{
				((FullNameNode)n).setRightOperandNode(e);
			}
		)?
	;
	exception catch [RecognitionException ex] { return handleMissingIdentifier(ex, n); }

/**
 * Matches a restricted name. For example:
 *
 *     my.package.name.Clock;
 *     private::myPrivateVar;
 *     UnqualifiedTypeClock;
 *
 */
restrictedName returns [ExpressionNodeBase nameExpression]
{
    nameExpression = null;
 	IdentifierNode placeHolderRightNode = null;
    ASToken opToken = null;
    ExpressionNodeBase part = null;
}
    :   nameExpression=restrictedNamePart
    	
    	// LL(1) grammar can only branch on the next token. 
        // The LA(2) semantic predicate is used to disambiguate:
        // 1. "foo.bar" - a restricted name consisting two identifiers
        // 2. "foo.(bar)" - a member expression whose left-hand side is an identifier
        //                  and the right-hand side is a parenthesis expression
        (options { greedy=true; }: { LA(2) != TOKEN_PAREN_OPEN }?
            {
                opToken = LT(1); 
                
                // The place-holder node is a safe-net in case parsing the 
                // "right" node fails, so that we will still have a balanced
                // FullNameNode.
                placeHolderRightNode = IdentifierNode.createEmptyIdentifierNodeAfterToken(opToken);
                
                final ExpressionNodeBase nameLeft = nameExpression;
            }
            (   TOKEN_OPERATOR_MEMBER_ACCESS 
                { nameExpression = new FullNameNode(nameLeft, opToken, placeHolderRightNode); } 
            |   TOKEN_OPERATOR_NS_QUALIFIER 
                { nameExpression = new NamespaceAccessExpressionNode(nameLeft, opToken, placeHolderRightNode); }
            )
            
            (	{ opToken.getType() == TOKEN_OPERATOR_NS_QUALIFIER && LA(1) == TOKEN_SQUARE_OPEN }?
                // matches ns::["var_in_ns"]
            	nameExpression=bracketExpression[nameLeft]
	        |   part=restrictedNamePart
	            { 
	            	((BinaryOperatorNodeBase)nameExpression).setRightOperandNode(part);
	            	checkForChainedNamespaceQualifierProblem(opToken, part);
	        	}
        	)
        )*
    ;
	exception catch [RecognitionException ex] 
	{  
	   if (nameExpression == null)
	       nameExpression = handleMissingIdentifier(ex); 
       else
           consumeParsingError(ex);
    }
    
    
/**
 * Matches the identifier part of a restricted name. For example:
 * 
 *     private
 *     public
 *     foo
 *     MyType
 *
 */
restrictedNamePart returns [IdentifierNode id]
{
    id = null;
    final ASToken lt = LT(1);
}
    :   id=identifier
    |   TOKEN_NAMESPACE_NAME 
        { id = new IdentifierNode(lt); }
    |   TOKEN_KEYWORD_SUPER
        { id = LanguageIdentifierNode.buildSuper(lt); }
    ;
    // "identifier", "namespace name" and "super" are all "identifiers" to
    // the user. So we override the default error handling in order to emit
    // "expecting identifier but found ..." syntax problem.
    exception catch [NoViableAltException ex] { id = expectingIdentifier(ex); }

/**
 * Keep legacy rule for transpiling.
 */
typedNameOrStar returns [ExpressionNodeBase n]
    :    n=type
    ;
    
/**
 * Matches a type reference.
 *
 *     String
 *     int
 *     *
 *     Vector.<Clock>
 *     foo.bar.Vector.<T>
 *
 */
type returns [ExpressionNodeBase n]
{ 
    n = null; 
}
    :   n=starLiteral
    |   n=restrictedName ( n=typeApplication[n] )?
    ;	
    exception catch [RecognitionException ex] { n = handleMissingIdentifier(ex); }
  
/**
 * Matches a "type application" part>
 *
 *     .<String>
 *     .<Clock>
 *     .<uint>
 *
 */
typeApplication [ExpressionNodeBase root] returns[TypedExpressionNode n]
{ 
    n = null; 
    ExpressionNodeBase t = null; 
    Token closeT = null;
    enterTypeApplication(root);
}
    :   openT:TOKEN_TYPED_COLLECTION_OPEN
        t=type
        {
            n = new TypedExpressionNode(root, t, (ASToken)openT);
            closeT = LT(1);
        }
        ( TOKEN_TYPED_COLLECTION_CLOSE | TOKEN_OPERATOR_GREATER_THAN )
        { n.endAfter(closeT); }
    ; 
    exception catch [RecognitionException ex] { consumeParsingError(ex); } 


/**
 * Matches an optional expression.
 * @return NilNode or ExpressionNodeBase.
 */	 		
optExpression returns[ExpressionNodeBase e]
{ 
	e = null; 
}
	:   (options{greedy=true;}: e=expression)?												
		{ if (e == null) e = new NilNode(); }
	;
	exception catch [RecognitionException ex] { handleParsingError(ex); }
		
/**
 * Matches an expression or a comma-separated expression list.
 */
expression returns [ExpressionNodeBase n]
{
    n = null; 
    ExpressionNodeBase e1 = null; 
}
    :   n=assignmentExpression 
    	(options{greedy=true;}: 
            op:TOKEN_COMMA 
            e1=assignmentExpression
            { n = BinaryOperatorNodeBase.create((ASToken)op,n,e1); }
    	)*
    ;	
    exception catch [RecognitionException ex] { handleParsingError(ex); }

/**
 * Matches an "assignment expression".
 *
 * According to ASL sytax spec, the productions for an "assignment expression"
 * is either a "conditional expression" or a "left-hand side expression" followed
 * by an "assignment operator" and an "assignment expression". However, since
 * "assignmentExpression" and "conditionaExpression" is ambiguous at indefinite 
 * look-ahead distance, this LL(1) grammar can't decide which alternative to
 * choose. As a result, the implementation is more lenient in that an AST node
 * for an assignment binary node will be built even the left-hand side expression
 * is not a valid "LeftHandSideExpression", such as a constant.
 * 
 * For example:
 * <code>100 = "hello";</code>
 * This statement will be parsed without syntax error, generating tree like:
 * <pre>
 *        =
 *       / \
 *    100  "hello"
 * </pre>
 *
 * A possible solution to this is to find out the difference between "conditional
 * expression" and "left-hand side expression", then insert a semantic predicate
 * before matching a "assignment operator".
 */
assignmentExpression returns [ExpressionNodeBase n]
{
	n = null; 
	ASToken op = null; 
	ExpressionNodeBase r = null;
}		
	:   n=condExpr 
		(options{greedy=true;}: 
		    op=assignOp 
		    r=assignmentRightValue
	        { n = BinaryOperatorNodeBase.create(op,n,r); }
		)?
	;
	
/**
 * Matches the right-hand side of an assignment expression.
 * "public" namespace is allowed as an R-value for backward compatibility.
 * @see "CMP-335 and ASLSPEC-19"
 */
assignmentRightValue returns [ExpressionNodeBase rightExpr]
{
	rightExpr = null;
}
    :   { isNextTokenPublicNamespace() }? p:TOKEN_NAMESPACE_ANNOTATION
        { rightExpr = new NamespaceIdentifierNode((ASToken)p); }
    |   rightExpr=assignmentExpression  
    ;
	
assignOp returns [ASToken op]
{ 
	op = LT(1); 
}
    :   TOKEN_OPERATOR_ASSIGNMENT 
    |   TOKEN_OPERATOR_LOGICAL_AND_ASSIGNMENT
    |   TOKEN_OPERATOR_LOGICAL_OR_ASSIGNMENT
    |   TOKEN_OPERATOR_PLUS_ASSIGNMENT
    |   TOKEN_OPERATOR_MINUS_ASSIGNMENT
    |   TOKEN_OPERATOR_MULTIPLICATION_ASSIGNMENT
    |   TOKEN_OPERATOR_DIVISION_ASSIGNMENT
    |   TOKEN_OPERATOR_MODULO_ASSIGNMENT
    |   TOKEN_OPERATOR_BITWISE_AND_ASSIGNMENT
    |   TOKEN_OPERATOR_BITWISE_OR_ASSIGNMENT
    |   TOKEN_OPERATOR_BITWISE_XOR_ASSIGNMENT
    |   TOKEN_OPERATOR_BITWISE_LEFT_SHIFT_ASSIGNMENT
    |   TOKEN_OPERATOR_BITWISE_RIGHT_SHIFT_ASSIGNMENT
    |   TOKEN_OPERATOR_BITWISE_UNSIGNED_RIGHT_SHIFT_ASSIGNMENT
    ;

/**
 * Matches a ternary expression such as: 
 *
 *     (x > 2) ? "greater" : "smaller"
 */		
condExpr returns [ExpressionNodeBase n]
{
    n = null; 
    ExpressionNodeBase trueExpr = null; 
    ExpressionNodeBase falseExpr = null;
    TernaryOperatorNode ternary = null;
}
	:   n=binaryExpr 
        (   op:TOKEN_OPERATOR_TERNARY       
            {
                ternary = new TernaryOperatorNode((ASToken)op,n,null,null);
                n = ternary; 
            }
            trueExpr=assignmentExpression   { ternary.setLeftOperandNode(trueExpr); }
            TOKEN_COLON 
            falseExpr=assignmentExpression  { ternary.setRightOperandNode(falseExpr); }
        )?
        exception catch [RecognitionException ex] { handleParsingError(ex); }
	;
	
/**
 * Binary expression uses operator precedence parser in BaseASParser.
 */
binaryExpr returns [ExpressionNodeBase n]
{
	n = precedenceParseExpression(4);
	if (true)	return n;
}
	: fakeExpr
	;
	
/**
 * fakeExpr simulates the set of allowable follow tokens in an expression context, which allows antlr to function.
 * It is unreachable.
 */
fakeExpr
{ 
	ExpressionNodeBase n = null; 
}
	:	n=unaryExpr (options{greedy=true;}: binaryOperators fakeExpr)?
	;
	
/**
 * Declares all the binary operators.
 */
binaryOperators
    :   TOKEN_OPERATOR_LOGICAL_OR 
    |   TOKEN_OPERATOR_LOGICAL_AND
    |   TOKEN_OPERATOR_BITWISE_OR 
    |   TOKEN_OPERATOR_BITWISE_XOR 
    |   TOKEN_OPERATOR_BITWISE_AND
    |   TOKEN_OPERATOR_EQUAL 
    |   TOKEN_OPERATOR_NOT_EQUAL 
    |   TOKEN_OPERATOR_STRICT_EQUAL 
    |   TOKEN_OPERATOR_STRICT_NOT_EQUAL
    |   TOKEN_OPERATOR_GREATER_THAN 
    |   TOKEN_OPERATOR_GREATER_THAN_EQUALS
    |   TOKEN_OPERATOR_LESS_THAN 
    |   TOKEN_OPERATOR_LESS_THAN_EQUALS
    |   TOKEN_KEYWORD_INSTANCEOF
    |   TOKEN_KEYWORD_IS 
    |   TOKEN_KEYWORD_AS 
    |   TOKEN_KEYWORD_IN
    |   TOKEN_OPERATOR_BITWISE_LEFT_SHIFT 
    |   TOKEN_OPERATOR_BITWISE_RIGHT_SHIFT 
    |   TOKEN_OPERATOR_BITWISE_UNSIGNED_RIGHT_SHIFT
    |   TOKEN_OPERATOR_MINUS 
    |   TOKEN_OPERATOR_PLUS
    |   TOKEN_OPERATOR_DIVISION 
    |   TOKEN_OPERATOR_MODULO 
    |   TOKEN_OPERATOR_STAR
	;	

/**
 * Matches a "prefix expression".
 *
 *     delete x[i]
 *     ++i
 *     --i
 *
 * The distinction between this rule and "unary expression" makes the parser
 * more strict about what expressions can follow what tokens.
 */
prefixExpression returns [ExpressionNodeBase n]
{
	n = null;
	final ASToken op = LT(1);
}
	:	n=postfixExpr
	|	(	TOKEN_KEYWORD_DELETE n=postfixExpr 
	    |   TOKEN_OPERATOR_INCREMENT n=lhsExpr
	    |   TOKEN_OPERATOR_DECREMENT n=lhsExpr
	    )
        { 
	    	if (n == null)
	            n = IdentifierNode.createEmptyIdentifierNodeAfterToken(op);
	    	n = UnaryOperatorNodeBase.createPrefix(op, n); 
    	} 
	;
    exception catch [RecognitionException ex] { handleParsingError(ex); }

/**
 * Matches a "unary expression".
 *
 * This rule is called out of the precedence parser in BaseASParser.
 * If you need to change the name of this rule, you'll also need to update 
 * the base class.
 */
unaryExpr returns [ExpressionNodeBase n]
{ 
    n = null; 
    ASToken op = null; 
}	
    :   (   n=prefixExpression 
        |   op=unaryOp n=unaryExpr 
            { 
            	if (n == null)
                    n = IdentifierNode.createEmptyIdentifierNodeAfterToken(op);
            	n = UnaryOperatorNodeBase.createPrefix(op, n); 
        	} 
        ) 
        (options { greedy = true; }: 
            n=propertyAccessExpression[n] 
        |   n=arguments[n] 
        )*
    ;
    exception catch [RecognitionException ex] { handleParsingError(ex); }

/**
 * Matches a unary operator.
 */
unaryOp returns [ASToken op]
{
	op = LT(1); 
}
    :   TOKEN_KEYWORD_VOID
    |   TOKEN_KEYWORD_TYPEOF
    |   TOKEN_OPERATOR_PLUS 
    |   TOKEN_OPERATOR_MINUS 
    |   TOKEN_OPERATOR_BITWISE_NOT 
    |   TOKEN_OPERATOR_LOGICAL_NOT
    ;
	   
/**
 * Matches "Postfix Expression" such as: i++, i--
 *
 * Since ECMA semicolon insertion rule requires that if a "++" or "--" is not
 * on the same line as its left-hand side expression, a semicolon is inserted
 * before the "--" or "++" token. The side-effect of the inserted semicolon is
 * to terminate the expression parsing at this point. As a result, we have to
 * return "null" to stop parsing the expression. An upstream production will
 * pickup the "--" or "++" by starting a new expression.
 *
 * A good test case for such situation would be:
 *
 *    var i = 99
 *    ++i
 *
 * A semicolon should be inserted after "99", resulting in two separate ASTs
 * for "var i=99" and "++i". Otherwise, "var i=99++" is a bad recognition.
 */
postfixExpr returns [ExpressionNodeBase n]
{
    n = null; 
    boolean isSemicolonInserted = false;
}
	: n=lhsExpr 
	  {
	      final ASToken nextToken = LT(1); 
	      if (nextToken.getType() == ASTokenTypes.TOKEN_OPERATOR_INCREMENT ||
	          nextToken.getType() == ASTokenTypes.TOKEN_OPERATOR_DECREMENT)
	          isSemicolonInserted = beforeRestrictedToken(nextToken);
      }
      (   {!isSemicolonInserted}? (options{greedy=true;}: n=postfixOp[n])?
      |   // Do nothing if optional semicolon is inserted.
          // This empty alternative is required because otherwise a semantic 
          // predicate exception will be thrown, leading the code enter error 
          // handling, which will create incorrect tree shape.
      )
	;

/**
 * Matches a "postfix" operator such as: ++, --
 * The parameter "n" is the expression the postfix operator acts on.
 * The return value "top" is a UnaryOperatorNode.
 */ 
postfixOp[ExpressionNodeBase n] returns [UnaryOperatorNodeBase top]
{
    final ASToken op = LT(1); 
    top = null;
}	
	:  ( TOKEN_OPERATOR_INCREMENT 
	   | TOKEN_OPERATOR_DECREMENT )
       { top = UnaryOperatorNodeBase.createPostfix(op, n); }
	;

/**
 * Matches a primary expression.
 */ 
primaryExpression returns [ExpressionNodeBase n]
{ 
    n = null;  
    ASToken token = LT(1);
}
    :   TOKEN_KEYWORD_NULL
        { n = new LiteralNode(token, LiteralType.NULL); }
    |   TOKEN_KEYWORD_TRUE
        { n = new LiteralNode(token, LiteralType.BOOLEAN); }
    |   TOKEN_KEYWORD_FALSE
        { n = new LiteralNode(token, LiteralType.BOOLEAN); }
    |   TOKEN_KEYWORD_THIS
        { n = LanguageIdentifierNode.buildThis(token); }
    |   token=numericLiteral
        { n = new NumericLiteralNode(token); }
    |   TOKEN_LITERAL_STRING
        { n = new LiteralNode(token, LiteralType.STRING); }
    |   TOKEN_VOID_0
        { n = new LiteralNode(token, LiteralType.OBJECT); }
    |   TOKEN_LITERAL_REGEXP
        { n = new RegExpLiteralNode(token, this); }
    |   { n = new ArrayLiteralNode(); } arrayInitializer[(ArrayLiteralNode)n]
    |   n=objectLiteralExpression
    |   n=xmlInitializer      { leaveXMLLiteral(); }
    |   n=xmlListInitializer  { leaveXMLLiteral(); }
    |   n=functionExpression 
    ;
    exception catch [RecognitionException ex] { handleParsingError(ex); }
	
/**
 * Matches a numeric literal token.
 */
numericLiteral returns [ASToken op]
{ 
	op = LT(1); 
}
    :   TOKEN_LITERAL_NUMBER 
    |   TOKEN_LITERAL_HEX_NUMBER
	;
	
/**
 * Matches an "object literal".
 */
objectLiteralExpression returns [ExpressionNodeBase n]
{ 
	ObjectLiteralNode o = new ObjectLiteralNode(); 
	n = o; 
	ContainerNode b = o.getContentsNode(); 
	ExpressionNodeBase vp = null;
}
    :   openT:TOKEN_BLOCK_OPEN           { n.startBefore(openT); }
        (   vp=objectLiteralValuePair    { b.addItem(vp); }
      	    (   TOKEN_COMMA vp=objectLiteralValuePair
    	        { if (vp != null) b.addItem(vp); }
     		    exception catch [RecognitionException ex] 
     		    { handleParsingError(ex); }
      	    )*
        )?		
        closeT:TOKEN_BLOCK_CLOSE         { n.endAfter(closeT); }  		
    ;
    exception catch [RecognitionException ex] { handleParsingError(ex); }

/**
 * Matches a "field" in an "object literal".
 * The "field" can be gated with a "config condition". If the condition is
 * "false", return "null" value; Otherwise, return the expression node of the
 * key/value pair.
 */
objectLiteralValuePair returns [ExpressionNodeBase n]
{ 
	ExpressionNodeBase v = null;  
	n = null;            
	boolean condition = true;   
    ASToken numberT = null;     
}
    :   (   { isConfigCondition() && LA(4) != TOKEN_COLON && LA(4) != TOKEN_BLOCK_CLOSE }?
            condition=configCondition
        |   // Skip - no config varaible.
        )
        
        // Field name:
        (   nameT:TOKEN_IDENTIFIER
        	{ n = new NonResolvingIdentifierNode(nameT != null ? nameT.getText() : "",nameT); }
        |   numberT=numericLiteral
        	{ n = new NumericLiteralNode(numberT); }
        |   stringT:TOKEN_LITERAL_STRING
        	{ n = new LiteralNode(LiteralType.STRING, stringT); }
        )
        
        c:TOKEN_COLON 
        
        // Field value:
        v=assignmentExpression 
    	{ 
    		if (condition)
    		    n = new ObjectLiteralValuePairNode((ASToken)c,n,v); 
		    else
		        n = null;
	    }
    ;
    exception catch [RecognitionException ex] { handleParsingError(ex);  }
 

/**
 * Matches array literal. For example:
 *
 *    []
 *    ["hello", 3.14, foo]
 *    [ , x, y]
 *    [ , ,]
 *
 * "Holes" (empty array elements) are allowed. See "arrayElements" rule for details.
 */ 
arrayInitializer [ArrayLiteralNode node] 
    {
        final ContainerNode contents = node.getContentsNode(); 
    }
    :   open:TOKEN_SQUARE_OPEN            { node.startBefore(open); contents.startAfter(open); }
        arrayElements[contents]
        close:TOKEN_SQUARE_CLOSE          { node.endAfter(close); contents.endBefore(close); }
    ;	
    exception catch [RecognitionException ex] 
    { 
    	// Do not convert keywords to identifiers.
    	// This is for recovering from:
    	// [
    	// var x:int;
    	handleParsingError(ex); 
    	// Notify the caller that the array literal failed.
    	throw ex; 
    }
	
/**
 * Matches all the elements in an "arrayInitializer". For example:
 *
 *    x,y,z
 *    x,,
 *    (empty)
 *    ,,,,,,
 *
 * "Holes" are compiled as "undefined".
 * Leading "holes" are kept as "undefined" values.
 * "Holes" in the middle are kept as "undefined" values.
 * Trailing "holes" are kept as "undefined" values except that the last "hole"
 * is dropped.
 *
 * For example: x=[,,1,,,2,,,] has 2 leading holes, 2 holes in the middle, and 3
 * holes at the end. All the holes except for the last trailing holes are kept
 * as undefined values:
 *
 * x[0]=undefined
 * x[1]=undefined
 * x[2]=1
 * x[3]=undefined
 * x[4]=undefined
 * x[5]=2
 * x[6]=undefined
 * x[7]=undefined
 * (end)
 *
 */
arrayElements[ContainerNode b]
{ 
    ExpressionNodeBase e = null;  
}
    :   ( TOKEN_COMMA { b.addItem(new NilNode()); } )*
        (   { LA(1) != TOKEN_SQUARE_CLOSE}?   
            	e=arrayElement { b.addItem(e); /*1*/ }
                (   TOKEN_COMMA 
                    (   { LA(1) != TOKEN_SQUARE_CLOSE && LA(1) != TOKEN_COMMA }? e=arrayElement { b.addItem(e); /*2*/}
                    |   { LA(1) != TOKEN_SQUARE_CLOSE && LA(1) == TOKEN_COMMA }? { b.addItem(new NilNode()); }
                    |   // Next token is "]" - pass.
            	 	)
             		exception catch [RecognitionException ex] { handleParsingError(ex); }
              	)*
      	|    // Next token is "]" - the initializer is a list of commas: [,,,,,]
      	)
    ;
    exception catch [RecognitionException ex] { handleParsingError(ex); }

/**
 * Matches an "array element" in an "array literal". An array element can be 
 * gated with a config variable. If the config variable is false, the element
 * will be matched as a "hole" in the array literal.
 */
arrayElement returns [ExpressionNodeBase e]
{ 
	e = null; 
	boolean c = true;  // config variable
}
    :   (   { isConfigCondition() && LA(4) != TOKEN_COMMA && LA(4) != TOKEN_SQUARE_CLOSE }?
            c=configCondition
        |   // Skip - no config varaible.
        )
        e=assignmentExpression
        {
        	if (!c) 
        	{
        		final NilNode nilNode = new NilNode(); 
        		nilNode.span(e, e);
        		e = nilNode;
    		}
        }
    ;
    exception catch [RecognitionException ex] { handleParsingError(ex);  }
	
/**
 * Matches a "vector initializer".
 */
vectorLiteralExpression returns [VectorLiteralNode node] 
{ 
	node = new VectorLiteralNode(); 
	ContainerNode b = node.getContentsNode(); 
	ExpressionNodeBase type = null; 
}
	:   open:TOKEN_TYPED_LITERAL_OPEN      { node.endAfter(open); }
	 	type=type                          { node.setCollectionTypeNode(type); }
        close:TOKEN_TYPED_LITERAL_CLOSE    { node.endAfter(close); }
        openT:TOKEN_SQUARE_OPEN            { b.startAfter(openT); }
	 	(vectorLiteralContents[b])?
        closeT:TOKEN_SQUARE_CLOSE          { b.endBefore(closeT); }
	;
	exception catch [RecognitionException ex] { handleParsingError(ex); }

/**
 * Matches a "vector element" in a vector initializer.
 */
vectorLiteralContents[ContainerNode b]
{ 
	ExpressionNodeBase e = null;  
}
    :   e=arrayElement       { b.addItem(e); }
        (   TOKEN_COMMA
            {
                //  A trailing comma is allowed, but 
                //  an intermediate comma is not.
                if ( LA(1) != TOKEN_SQUARE_CLOSE ){
                    e=arrayElement();
                    b.addItem(e); 
                }
    	 	}
            exception catch [RecognitionException ex] { handleParsingError(ex); }
      	)*
    ;

/**
 * Matches "XML literal expression".
 */
xmlInitializer returns [XMLLiteralNode n]
{ 
	n = new XMLLiteralNode(); 
	final ASToken lt = LT(1);
	enterXMLLiteral();
}
    :   { LA(1) == TOKEN_E4X_COMMENT || 
    	  LA(1) == TOKEN_E4X_CDATA || 
    	  LA(1) == TOKEN_E4X_PROCESSING_INSTRUCTION 
    	}? 
        xmlMarkup
        { n.appendLiteralToken(lt); }
        xmlWhitespace[n]
    |   (options { greedy = true; }: xmlElementContent[n] )+
    ;
    exception catch [RecognitionException ex] { handleParsingError(ex); }	
    
/**
 * Match XML whitespace tokens. If {@code ContainerNode} is null, drop the
 * whitespace tokens.
 */
xmlWhitespace [BaseLiteralContainerNode n]
    :   (options { greedy = true; }: 
	        ws:TOKEN_E4X_WHITESPACE 
            { 
            	if (n != null) 
            	    n.appendLiteralToken((ASToken)ws); 
    	    }
        )*
	;
    
/**
 * Matches an XML comment, XML CDATA or XML PI token.
 */
xmlMarkup
    :   TOKEN_E4X_COMMENT
    |   TOKEN_E4X_CDATA
    |   TOKEN_E4X_PROCESSING_INSTRUCTION
    ;    
       
/**
 * Matches an E4X token that can be aggregated in "xmlTokenAggregated".
 * Instead of a full recursive descent parser for XML tags, the base class
 * uses a tag name stack to check matching tags. A complete parse tree with
 * XML structure is unnecessary and adds extra overhead to the parser.
 */
xmlToken [BaseLiteralContainerNode n]
{
	final ASToken t = LT(1);
}
    :   (   xmlMarkup
        |   TOKEN_E4X_WHITESPACE 
        |   TOKEN_E4X_ENTITY
        |   TOKEN_E4X_DECIMAL_ENTITY
        |   TOKEN_E4X_HEX_ENTITY 
        |   TOKEN_E4X_TEXT
        |   TOKEN_E4X_STRING
        )
        { n.appendLiteralToken(t); }       
    ;
    exception catch [RecognitionException ex] { handleParsingError(ex); }	
    
/**
 * Matches an XML tag.
 *
 *     <foo>
 *     </foo>
 *     <foo />
 *     <{name}>
 *     <foo name={nameValue}>
 *     <foo {attrs}>
 */
xmlTag [BaseLiteralContainerNode n]
    :   (   openT:TOKEN_E4X_OPEN_TAG_START          // <foo
            { 
            	xmlTagOpen((ASToken)openT); 
                n.appendLiteralToken((ASToken)openT);
        	} 
        |   closeT:TOKEN_E4X_CLOSE_TAG_START        // </foo
            { 
            	xmlTagClose((ASToken)closeT); 
                n.appendLiteralToken((ASToken)closeT);
        	} 
        |   openNoNameT:HIDDEN_TOKEN_E4X            // <
            { 
            	xmlTagOpenBinding((ASToken)openNoNameT); 
                n.appendLiteralToken((ASToken)openNoNameT);
        	}
        	
        	// Note about compatibility:
        	// x = <  tagName foo="bar" />;
        	//      ^
        	// Whitespace isn't allowed here according to ASL spec.
            // Avik from AS3 spec team confirmed it was a bug that the old ASC allowed it.

        	(   xmlContentBlock[n]
        	|   nT:TOKEN_E4X_NAME
        	    { n.appendLiteralToken((ASToken)nT); }
        	)
        )
        xmlWhitespace[n]
        (   ( { isXMLAttribute() }? xmlAttribute[n] 
	    | xmlContentBlock[n] )
            xmlWhitespace[n]
        )*
        (   endT:TOKEN_E4X_TAG_END                  // >
            { n.appendLiteralToken((ASToken)endT); }
        |   emptyEndT:TOKEN_E4X_EMPTY_TAG_END       // />
            {
            	xmlEmptyTagEnd((ASToken)emptyEndT); 
                n.appendLiteralToken((ASToken)emptyEndT);
            }
        )
    ;
    
/**
 * Matches an XML attribute.
 * 
 *     name="value"
 *     name='value'
 *     name={value}
 *     {name}="value"
 *     {name}='value'
 *     {name}={value}
 */
xmlAttribute [BaseLiteralContainerNode n]
    :   (   nT:TOKEN_E4X_NAME  
            { n.appendLiteralToken((ASToken)nT); }
        |   nsT:TOKEN_E4X_XMLNS 
            { n.appendLiteralToken((ASToken)nsT); }
        |   xmlAttributeBlock[n]
        ) 
        (   dT:TOKEN_E4X_NAME_DOT 
            { n.appendLiteralToken((ASToken)dT); }
            dnT:TOKEN_E4X_DOTTED_NAME_PART 
            { n.appendLiteralToken((ASToken)dnT); }
        )* 
        xmlWhitespace[n]
        eqT:TOKEN_E4X_EQUALS
        { n.appendLiteralToken((ASToken)eqT); }
        xmlWhitespace[n]
        (options { greedy = true; }:
            strT:TOKEN_E4X_STRING
            { n.appendLiteralToken((ASToken)strT); }
        |   eT:TOKEN_E4X_ENTITY
            { n.appendLiteralToken((ASToken)eT); }
        |   hexT:TOKEN_E4X_HEX_ENTITY
            { n.appendLiteralToken((ASToken)hexT); }
        |   xmlContentBlock[n]
        )+
    ;

/**
 * Matches an expression block in XML literals.
 *
 *    <foo>{ this.fooValue }</foo>
 */
xmlElementContent [BaseLiteralContainerNode n]
    :   xmlToken[n] 
    |   xmlContentBlock[n]
    |   xmlTag[n]
    ;
    
/**
 * Matches an E4X XML list expression.
 */
xmlListInitializer returns [XMLListLiteralNode n]
{ 
	n = new XMLListLiteralNode(); 
	enterXMLLiteral();
}
    :   xmlListT:TOKEN_LITERAL_XMLLIST 
    	{ n.getContentsNode().addItem(new LiteralNode(LiteralType.XML, xmlListT)); }      
        ( xmlElementContent[n] )*
        closeT: TOKEN_E4X_XMLLIST_CLOSE 
    	{ n.getContentsNode().addItem(new LiteralNode(LiteralType.XML, closeT)); }      
    	xmlWhitespace[null]
    ;
    exception catch [RecognitionException ex] { handleParsingError(ex); }	

/**
 * Matches a binding expression in an XML literal.
 */
xmlContentBlock[BaseLiteralContainerNode n]
{ 
	ExpressionNodeBase e = null; 
}
    :   TOKEN_E4X_BINDING_OPEN 
        e=expression 
    	{ 
            if(e != null) 
                n.getContentsNode().addItem(e); 
    	}
        TOKEN_E4X_BINDING_CLOSE
    ;
    exception catch [RecognitionException ex] { handleParsingError(ex); }
    

/**
 * Matches a binding expression in an XML literal attribute name.
 */
xmlAttributeBlock[BaseLiteralContainerNode n]
{ 
	ExpressionNodeBase e = null; 
}
    :   TOKEN_E4X_BINDING_OPEN 
        e=lhsExpr 
    	{ 
            if(e != null) 
                n.getContentsNode().addItem(e); 
    	}
        TOKEN_E4X_BINDING_CLOSE
    ;
    exception catch [RecognitionException ex] { handleParsingError(ex); }
    

/**
 * Matches a left-hand side (of asssignment) expression.
 */
lhsExpr returns [ExpressionNodeBase n]
{
	n = null;
}
    :   (   n=newExpression 
        |   n=parenExpression 
        |   n=nameExpression 
        |   n=primaryExpression 
        |   n=xmlAttributeName
        )
    	(options { greedy = true; }: 
            n=propertyAccessExpression[n] 
        |   n=arguments[n] 
    	)*	
    ;
	exception catch [RecognitionException ex] { handleParsingError(ex); }
	
/**
 * Matches a member expression. See ASL syntax spec for details.
 */
memberExpression returns [ExpressionNodeBase n]
    :   (   n=primaryExpression
        |   n=parenExpression
        |   n=propertyName
        |   n=newExpression
        )
        ( options { greedy = true; }: n=propertyAccessExpression[n] )*
    ;

/**
 * Matches a new expression. See ASL syntax spec for details.
 */			
newExpression returns[ExpressionNodeBase n]
{ 
	n = null; 
}
	:   newT:TOKEN_KEYWORD_NEW 
        (   { LA(1) != TOKEN_KEYWORD_FUNCTION }?
        	(   n=vectorLiteralExpression
            |   n=memberExpression 
            )   
            {   
                if (n == null) 
                	n= handleMissingIdentifier(null); 
            	else 
            		n = FullNameNode.toMemberAccessExpressionNode(n);
				n = new FunctionCallNode((ASToken)newT, n); 
			} 
            (options{greedy=true;}: n=arguments[n])?
        |   n=functionExpression  { n = new FunctionCallNode((ASToken)newT, n); }
        )
		exception catch [RecognitionException ex] { 
			//if we have the 'new' keyword, but no expression, drop in a dummy identifier
		    if(newT != null && n == null) {
			    IdentifierNode identifier = handleMissingIdentifier(ex);
			    if(identifier != null) {
			        //if we're here, that means identifier fixup is turned on
			        n = new FunctionCallNode((ASToken)newT, identifier);
			    }
			} else {
			    handleParsingError(ex); 
			}
		}
	;

/**
 * Matches an expression with parenthesis.
 *
 *     (id)
 *     (1 + 2)
 *     (name == "hello")
 *
 */
parenExpression returns[ExpressionNodeBase n]
{
    n = null; 
} 
    :   TOKEN_PAREN_OPEN n=expression TOKEN_PAREN_CLOSE
	    { if(n != null) n.setHasParenthesis(true); }
    ;
    exception catch [RecognitionException ex] { handleParsingError(ex); }

/**
 * Matches a property name in a member expression.
 */
propertyName returns [ExpressionNodeBase n]
{
    n = null;
}
    :   n=starLiteral                 // *
    |   n=restrictedName              // qualified & unqualified name
    |   n=xmlAttributeName            // e4x attribute
    ;
    
/**
 * This is a non-greedy and non-aggregating version of "restricted name".
 * It is defined in addition to "propertyName" in order to get correct
 * precedence in the name expressions and expected tree shapes.
 */
nameExpression returns[ExpressionNodeBase n]
	{ n = null; }
	
	: n=identifier 
	| n=starLiteral
	| superT:TOKEN_KEYWORD_SUPER
		{ n = LanguageIdentifierNode.buildSuper((IASToken)superT); }
	| nsT:TOKEN_NAMESPACE_NAME
		{ 
			n = new NamespaceIdentifierNode((ASToken)nsT); 
			((NamespaceIdentifierNode)n).setIsConfigNamespace(isConfigNamespace((NamespaceIdentifierNode)n));	
		}
	;	
	
	
/**
 * Matches an E4X attribute name. For example:
 *  
 *     "@*", @data, @[foo="hello"]
 *
 */
xmlAttributeName returns [ExpressionNodeBase result]
{
    result = UnaryOperatorNodeBase.createPrefix(LT(1), null);
    ExpressionNodeBase e = null;
}
    :   TOKEN_OPERATOR_ATSIGN
        (	(   e=starLiteral                
	        |   e=identifier                 
	        |   nsT:TOKEN_NAMESPACE_NAME     { e = new NamespaceIdentifierNode((ASToken)nsT); }
	        )
	        { 
	        	((UnaryOperatorNodeBase)result).setExpression(e); 
        	}
        |   result=bracketExpression[result]
        )
    ;

/**
 * Matches a property access expression:
 * For example: (assuming 'foo' is already matched)
 *     foo.bar
 *     foo..bar
 *     foo::bar
 *     foo[bar]
 *     foo.<bar>
 */  	
propertyAccessExpression [ExpressionNodeBase l] returns [ExpressionNodeBase n]
{ 
    n = null;  
    ExpressionNodeBase r = null; 
    final ASToken op = LT(1); 
}
    :   TOKEN_OPERATOR_MEMBER_ACCESS r=accessPart 
        { n = new MemberAccessExpressionNode(l, op, r); }
    |   TOKEN_OPERATOR_DESCENDANT_ACCESS r=accessPart
        { n = new MemberAccessExpressionNode(l, op, r); }
    |   TOKEN_OPERATOR_NS_QUALIFIER r=nsAccessPart
        { n = transformToNSAccessExpression(l, op, r); } 
    |   n=bracketExpression[l]
    |   n=typeApplication[l]
    ;
	
/**
 * Matches parts after the dot in a property access expression.
 */
accessPart returns [ExpressionNodeBase n]
{
    n = null; 
}
	:   n=nameExpression
	|   n=xmlAttributeName 
	|   n=parenExpression 
	;
	exception catch [RecognitionException ex] { n = handleMissingIdentifier(ex);  }

/**
 * Matches parts after "::" in a property access expression.
 */
nsAccessPart returns [ExpressionNodeBase n]
{
    n = null; 
}
	:   n=nameExpression
	|   n=xmlAttributeName 
	|   n=parenExpression 
	|   n=runtimeName
	;
	exception catch [RecognitionException ex] { n = handleMissingIdentifier(ex);  }
	
/**
 * Matches a runtime attribute name.
 * 
 *     foo["name"]
 */
runtimeName returns[ExpressionNodeBase n]
{  
	ExpressionNodeBase e = null; 
	n = null; 
}
    :   TOKEN_SQUARE_OPEN e=expression closeT:TOKEN_SQUARE_CLOSE
    	{ 
    		n = new RuntimeNameExpressionNode(e);
    		n.endAfter(closeT); 
    	}
    ;
    exception catch [RecognitionException ex] { handleParsingError(ex); }

/**
 * Matches a "star" literal token, and create an IdentifierNode for "*".
 */
starLiteral returns [IdentifierNode id]
{
    id = LanguageIdentifierNode.buildAnyType(LT(1));
}
    :   TOKEN_OPERATOR_STAR
    ;

/**
 * Matches a "void" keyword token, and create an IdentifierNode for "void".
 */
voidLiteral returns [IdentifierNode id]
{
    id = LanguageIdentifierNode.buildVoid(LT(1));
}
    :   TOKEN_KEYWORD_VOID
    ;
	
/**
 * Matches the arguments of a function call: allcharacters between (...) 
 * including the parenthesis.
 *
 *    foo(true, 10, "hello");
 *    new MyData(100, "hundred");
 *
 * "newExpression" rule always creates a FunctionCallNode and passes it in as
 * the "root". Since the "newExpression" rule uses "arguments" rule to consume
 * its arguments, we don't need to create a new FunctionCallNode because the
 * "root" parameter is the FunctionCallNode to which the arguments belongs.
 *
 * On the other hand, if the "root" argument is not a FunctionCallNode from a
 * "new" expression, then the "root" can only be the "name expression" of a 
 * function call. As a result, we must create a FunctionCallNode for it. 
 * For example:
 *     myFunc() -- the name expression is identifier "myFunc"
 *     token.getCallBack()() -- the name expression is "token.getCallBack()"
 *                              expecting the return value of "getCallBack" to
 *                              be a function object.
 * 
 * [Shaoting] We could also make newExpression rule not create a 
 *            FunctionCallNode inside the "newExpression" rule, and pass in 
 *            the "new" token so that all FunctionCallNode are constructed in
 *            this rule. However, we want the parser to construct nodes as early
 *            as possible for IDE features. For example: "new T" without "(..)"
 *            would not result in a FunctionCallNode if we don't create it right
 *            after we see "T".
 */
arguments[ExpressionNodeBase root] returns[ExpressionNodeBase n]
{ 
	n = root; 
	ContainerNode args = null; 
}
    :   lpT:TOKEN_PAREN_OPEN
    	{
    	    final boolean isNewExpression = 
    	            (n instanceof FunctionCallNode) && 
    	            ((FunctionCallNode)n).isNewExpression();
            final boolean newFunctionCallAlreadyHasArgs = isNewExpression &&
	            ((FunctionCallNode)n).getArgumentsNode().getStart() != -1;
	            // the above line is a hack to try to catch "new" expressions
	            // where the class to instantiate is the result of a function
	            // call:  new someFunction(someArgs)(constructorParams)
	            // we check to see if the arg node as a start() value which
	            // means that the first argument list (someargs) was already
	            // processed.
    	    final FunctionCallNode oldNode = newFunctionCallAlreadyHasArgs ? (FunctionCallNode)n : null;
    		if (n == null || !isNewExpression || newFunctionCallAlreadyHasArgs ) 
    			n = new FunctionCallNode(n);
    		if (newFunctionCallAlreadyHasArgs) {
                    ((FunctionCallNode)n).setNewKeywordNode(oldNode.getNewKeywordNode());
                    oldNode.setNewKeywordNode(null);
                }
    		args = ((FunctionCallNode)n).getArgumentsNode();
    		args.startBefore(lpT);
    		args.endAfter(lpT);
    		disableSemicolonInsertion();
    	} 
    		
        ( argumentList[args] )
    
    ;	
    exception catch [RecognitionException ex]
    { 	
    	//only consume this error b/c we're looking for a missing ')'  Let the parser handle the next token, maybe it will correct us
    	consumeParsingError(ex); 
    	if ( args != null ){
    		if(ex instanceof NoViableAltException) {
    			args.endBefore(((NoViableAltException)ex).token);
    		} else if(ex instanceof MismatchedTokenException) {
    			args.endBefore(((MismatchedTokenException)ex).token);
    		} else {
    			endContainerAtError(ex, args);
    	}	
    	}	
    	enableSemicolonInsertion(); 
    }

/**
 * Matches an argument list in a function call arguments.
 *
 * For argument list, we want to support the case where the user might be in the 
 * middle of typing, which could mean a couple of things:
 * 1. The user adds an argument before another.
 * 2. After which could lead to comma where we don't want it.
 */	
argumentList[ContainerNode args]
{ 
	ExpressionNodeBase n = null; 
	boolean foundFirstArg = false; 
}
    :   (   n=assignmentExpression
    		{ foundFirstArg = true; if (args != null) args.addItem(n); }
        	exception catch [RecognitionException ex] 
    		{ 
    			n = handleMissingIdentifier(ex); 
    			if(n != null) {
    				foundFirstArg = true;  //we don't want to add a second error for this case
    				if (args != null) args.addItem(n);
    			}
    		}
    	)? //make this optional for malformed code handling, but we will log a parser error against it
    	
    	(  commaT:TOKEN_COMMA  
    		{ 
    			//if we didn't find the first arg, log an error
    			if(!foundFirstArg) {
    				logSyntaxError((ASToken)commaT);
    				foundFirstArg = true;
    				n = handleMissingIdentifier(null); 
    				if (n!= null && args != null) args.addItem(n);
    			}
    			if (args != null) args.endAfter(commaT);	
    		}
    	
    		n=assignmentExpression
			{ 
				if(n == null) 
				    n = handleMissingIdentifier(null); 
			    
			    if (args != null) 
                    args.addItem(n); 
            }
    	
    		exception catch [RecognitionException ex] 
			{ 
				n = handleMissingIdentifier(ex); 
				if(n != null && args != null) {
					args.addItem(n);
				}
			}

    	)*

		rpT:TOKEN_PAREN_CLOSE
    		{ args.endAfter(rpT); enableSemicolonInsertion(); }

    ;
		
/**
 * Matches a bracket expression. For example:
 *
 *     [10]
 *     [idx]
 *
 */
bracketExpression [ExpressionNodeBase root] returns [DynamicAccessNode result]
{ 
    result = new DynamicAccessNode(root); 
    ExpressionNodeBase e = null; 
}
    :   TOKEN_SQUARE_OPEN 
        e=expression 
        (   closeT:TOKEN_SQUARE_CLOSE
            exception catch [RecognitionException ex] { handleParsingError(ex); }
        )
        { 
            result.setRightOperandNode(e); 
            result.endAfter(closeT); 
        }
    ;
    exception catch [RecognitionException ex] { handleParsingError(ex); }
