You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by cd...@apache.org on 2016/04/18 15:32:01 UTC
[12/14] git commit: [flex-falcon]
[refs/heads/feature/maven-migration-test] - Merge branches 'develop' and
'feature/maven-migration-test' of
https://git-wip-us.apache.org/repos/asf/flex-falcon into
feature/maven-migration-test
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/beaf5b63/compiler/src/main/antlr/org/apache/flex/compiler/internal/parsing/as/ASParser.g
----------------------------------------------------------------------
diff --cc compiler/src/main/antlr/org/apache/flex/compiler/internal/parsing/as/ASParser.g
index 34cb240,0000000..4d4936b
mode 100644,000000..100644
--- a/compiler/src/main/antlr/org/apache/flex/compiler/internal/parsing/as/ASParser.g
+++ b/compiler/src/main/antlr/org/apache/flex/compiler/internal/parsing/as/ASParser.g
@@@ -1,3223 -1,0 +1,3223 @@@
+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.flex.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.flex.compiler.tree.as.*;
+import org.apache.flex.compiler.workspaces.IWorkspace;
+import org.apache.flex.compiler.parsing.IASToken;
+import org.apache.flex.compiler.parsing.IASToken.ASTokenKind;
+import org.apache.flex.compiler.tree.as.IContainerNode.ContainerType;
+import org.apache.flex.compiler.tree.as.ILiteralNode.LiteralType;
+import org.apache.flex.compiler.internal.tree.as.*;
+import org.apache.flex.compiler.internal.tree.as.metadata.*;
+import org.apache.flex.compiler.asdoc.IASParserASDocDelegate;
+import org.apache.flex.compiler.constants.IASLanguageConstants;
+import org.apache.flex.compiler.problems.ICompilerProblem;
+import org.apache.flex.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.flex.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;
+}
+ : importT:TOKEN_KEYWORD_IMPORT
+ {
+ i = new ImportNode((ExpressionNodeBase) null);
+ i.startBefore(importT);
+ i.endAfter(importT);
+ c.addItem(i);
+ }
+
+ n=importName
+ {
+ if(n != null) {
+ 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.flex
+ *
+ * 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();
+}
+ : TOKEN_BLOCK_OPEN caseClauses[b] TOKEN_BLOCK_CLOSE
+ ;
+
+/**
+ * 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 Falcon-JS.
+ */
+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 { contents.startBefore(open); }
++ : open:TOKEN_SQUARE_OPEN { node.startBefore(open); contents.startAfter(open); }
+ arrayElements[contents]
- close:TOKEN_SQUARE_CLOSE { contents.endAfter(close); }
++ 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((NamespaceIdentifie
<TRUNCATED>