| Index: dart/compiler/java/com/google/dart/compiler/parser/DartParser.java
|
| diff --git a/dart/compiler/java/com/google/dart/compiler/parser/DartParser.java b/dart/compiler/java/com/google/dart/compiler/parser/DartParser.java
|
| deleted file mode 100644
|
| index cd5728ae4c43aa689e2e1bd76813179a201ddf93..0000000000000000000000000000000000000000
|
| --- a/dart/compiler/java/com/google/dart/compiler/parser/DartParser.java
|
| +++ /dev/null
|
| @@ -1,5688 +0,0 @@
|
| -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
| -// for details. All rights reserved. Use of this source code is governed by a
|
| -// BSD-style license that can be found in the LICENSE file.
|
| -
|
| -package com.google.dart.compiler.parser;
|
| -
|
| -import com.google.common.annotations.VisibleForTesting;
|
| -import com.google.common.collect.ImmutableSet;
|
| -import com.google.common.io.CharStreams;
|
| -import com.google.dart.compiler.DartCompilationError;
|
| -import com.google.dart.compiler.DartCompilerListener;
|
| -import com.google.dart.compiler.DartSource;
|
| -import com.google.dart.compiler.ErrorCode;
|
| -import com.google.dart.compiler.ErrorSeverity;
|
| -import com.google.dart.compiler.InternalCompilerException;
|
| -import com.google.dart.compiler.LibrarySource;
|
| -import com.google.dart.compiler.PackageLibraryManager;
|
| -import com.google.dart.compiler.Source;
|
| -import com.google.dart.compiler.ast.DartAnnotation;
|
| -import com.google.dart.compiler.ast.DartArrayAccess;
|
| -import com.google.dart.compiler.ast.DartArrayLiteral;
|
| -import com.google.dart.compiler.ast.DartAssertStatement;
|
| -import com.google.dart.compiler.ast.DartBinaryExpression;
|
| -import com.google.dart.compiler.ast.DartBlock;
|
| -import com.google.dart.compiler.ast.DartBooleanLiteral;
|
| -import com.google.dart.compiler.ast.DartBreakStatement;
|
| -import com.google.dart.compiler.ast.DartCascadeExpression;
|
| -import com.google.dart.compiler.ast.DartCase;
|
| -import com.google.dart.compiler.ast.DartCatchBlock;
|
| -import com.google.dart.compiler.ast.DartClass;
|
| -import com.google.dart.compiler.ast.DartClassTypeAlias;
|
| -import com.google.dart.compiler.ast.DartConditional;
|
| -import com.google.dart.compiler.ast.DartContinueStatement;
|
| -import com.google.dart.compiler.ast.DartDeclaration;
|
| -import com.google.dart.compiler.ast.DartDefault;
|
| -import com.google.dart.compiler.ast.DartDirective;
|
| -import com.google.dart.compiler.ast.DartDoWhileStatement;
|
| -import com.google.dart.compiler.ast.DartDoubleLiteral;
|
| -import com.google.dart.compiler.ast.DartEmptyStatement;
|
| -import com.google.dart.compiler.ast.DartExportDirective;
|
| -import com.google.dart.compiler.ast.DartExprStmt;
|
| -import com.google.dart.compiler.ast.DartExpression;
|
| -import com.google.dart.compiler.ast.DartField;
|
| -import com.google.dart.compiler.ast.DartFieldDefinition;
|
| -import com.google.dart.compiler.ast.DartForInStatement;
|
| -import com.google.dart.compiler.ast.DartForStatement;
|
| -import com.google.dart.compiler.ast.DartFunction;
|
| -import com.google.dart.compiler.ast.DartFunctionExpression;
|
| -import com.google.dart.compiler.ast.DartFunctionObjectInvocation;
|
| -import com.google.dart.compiler.ast.DartFunctionTypeAlias;
|
| -import com.google.dart.compiler.ast.DartIdentifier;
|
| -import com.google.dart.compiler.ast.DartIfStatement;
|
| -import com.google.dart.compiler.ast.DartImportDirective;
|
| -import com.google.dart.compiler.ast.DartInitializer;
|
| -import com.google.dart.compiler.ast.DartIntegerLiteral;
|
| -import com.google.dart.compiler.ast.DartLabel;
|
| -import com.google.dart.compiler.ast.DartLibraryDirective;
|
| -import com.google.dart.compiler.ast.DartMapLiteral;
|
| -import com.google.dart.compiler.ast.DartMapLiteralEntry;
|
| -import com.google.dart.compiler.ast.DartMethodDefinition;
|
| -import com.google.dart.compiler.ast.DartMethodInvocation;
|
| -import com.google.dart.compiler.ast.DartNamedExpression;
|
| -import com.google.dart.compiler.ast.DartNativeBlock;
|
| -import com.google.dart.compiler.ast.DartNativeDirective;
|
| -import com.google.dart.compiler.ast.DartNewExpression;
|
| -import com.google.dart.compiler.ast.DartNode;
|
| -import com.google.dart.compiler.ast.DartNodeWithMetadata;
|
| -import com.google.dart.compiler.ast.DartNullLiteral;
|
| -import com.google.dart.compiler.ast.DartParameter;
|
| -import com.google.dart.compiler.ast.DartParameterizedTypeNode;
|
| -import com.google.dart.compiler.ast.DartParenthesizedExpression;
|
| -import com.google.dart.compiler.ast.DartPartOfDirective;
|
| -import com.google.dart.compiler.ast.DartPropertyAccess;
|
| -import com.google.dart.compiler.ast.DartRedirectConstructorInvocation;
|
| -import com.google.dart.compiler.ast.DartReturnBlock;
|
| -import com.google.dart.compiler.ast.DartReturnStatement;
|
| -import com.google.dart.compiler.ast.DartSourceDirective;
|
| -import com.google.dart.compiler.ast.DartStatement;
|
| -import com.google.dart.compiler.ast.DartStringInterpolation;
|
| -import com.google.dart.compiler.ast.DartStringLiteral;
|
| -import com.google.dart.compiler.ast.DartSuperConstructorInvocation;
|
| -import com.google.dart.compiler.ast.DartSuperExpression;
|
| -import com.google.dart.compiler.ast.DartSwitchMember;
|
| -import com.google.dart.compiler.ast.DartSwitchStatement;
|
| -import com.google.dart.compiler.ast.DartSyntheticErrorExpression;
|
| -import com.google.dart.compiler.ast.DartSyntheticErrorIdentifier;
|
| -import com.google.dart.compiler.ast.DartSyntheticErrorStatement;
|
| -import com.google.dart.compiler.ast.DartThisExpression;
|
| -import com.google.dart.compiler.ast.DartThrowExpression;
|
| -import com.google.dart.compiler.ast.DartTryStatement;
|
| -import com.google.dart.compiler.ast.DartTypeExpression;
|
| -import com.google.dart.compiler.ast.DartTypeNode;
|
| -import com.google.dart.compiler.ast.DartTypeParameter;
|
| -import com.google.dart.compiler.ast.DartUnaryExpression;
|
| -import com.google.dart.compiler.ast.DartUnit;
|
| -import com.google.dart.compiler.ast.DartUnqualifiedInvocation;
|
| -import com.google.dart.compiler.ast.DartVariable;
|
| -import com.google.dart.compiler.ast.DartVariableStatement;
|
| -import com.google.dart.compiler.ast.DartWhileStatement;
|
| -import com.google.dart.compiler.ast.HasObsoleteMetadata;
|
| -import com.google.dart.compiler.ast.ImportCombinator;
|
| -import com.google.dart.compiler.ast.ImportHideCombinator;
|
| -import com.google.dart.compiler.ast.ImportShowCombinator;
|
| -import com.google.dart.compiler.ast.LibraryNode;
|
| -import com.google.dart.compiler.ast.LibraryUnit;
|
| -import com.google.dart.compiler.ast.Modifiers;
|
| -import com.google.dart.compiler.metrics.CompilerMetrics;
|
| -import com.google.dart.compiler.parser.DartScanner.Location;
|
| -import com.google.dart.compiler.resolver.Elements;
|
| -import com.google.dart.compiler.util.Lists;
|
| -import com.google.dart.compiler.util.apache.StringUtils;
|
| -
|
| -import java.io.IOException;
|
| -import java.io.Reader;
|
| -import java.math.BigInteger;
|
| -import java.text.Normalizer;
|
| -import java.util.ArrayList;
|
| -import java.util.Collections;
|
| -import java.util.HashSet;
|
| -import java.util.List;
|
| -import java.util.Set;
|
| -
|
| -/**
|
| - * The Dart parser. Parses a single compilation unit and produces a {@link DartUnit}.
|
| - * The grammar rules are taken from Dart.g revision 557.
|
| - */
|
| -public class DartParser extends CompletionHooksParserBase {
|
| -
|
| - private final Source source;
|
| - private final String sourceCode;
|
| - private final boolean isDietParse;
|
| - private final Set<String> prefixes;
|
| - private boolean allowNativeKeyword;
|
| - private final Set<Integer> errorHistory = new HashSet<Integer>();
|
| - private boolean isParsingInterface;
|
| - private boolean isTopLevelAbstract;
|
| - private int topLevelAbstractModifierPosition;
|
| - private boolean isParsingClass;
|
| - private int errorCount = 0;
|
| -
|
| - /**
|
| - * Determines the maximum number of errors before terminating the parser. See
|
| - * {@link #reportError(int, ErrorCode, Object...)}.
|
| - */
|
| - final int MAX_DEFAULT_ERRORS = Short.MAX_VALUE;
|
| -
|
| - // Pseudo-keywords that should also be valid identifiers.
|
| - private static final String ABSTRACT_KEYWORD = "abstract";
|
| - private static final String AS_KEYWORD = "as";
|
| - private static final String CALL_KEYWORD = "call";
|
| - public static final String DYNAMIC_KEYWORD = "dynamic";
|
| - private static final String EXPORT_KEYWORD = "export";
|
| - private static final String EXTERNAL_KEYWORD = "external";
|
| - private static final String FACTORY_KEYWORD = "factory";
|
| - private static final String GETTER_KEYWORD = "get";
|
| - private static final String HIDE_KEYWORD = "hide";
|
| - private static final String IMPLEMENTS_KEYWORD = "implements";
|
| - private static final String IMPORT_KEYWORD = "import";
|
| - private static final String INTERFACE_KEYWORD = "interface";
|
| - private static final String LIBRARY_KEYWORD = "library";
|
| - private static final String NATIVE_KEYWORD = "native";
|
| - private static final String OF_KEYWORD = "of";
|
| - private static final String ON_KEYWORD = "on";
|
| - private static final String OPERATOR_KEYWORD = "operator";
|
| - private static final String PART_KEYWORD = "part";
|
| - private static final String PREFIX_KEYWORD = "prefix";
|
| - private static final String SETTER_KEYWORD = "set";
|
| - private static final String SHOW_KEYWORD = "show";
|
| - private static final String STATIC_KEYWORD = "static";
|
| - private static final String TYPEDEF_KEYWORD = "typedef";
|
| - // does not exist in specification
|
| - private static final String PATCH_KEYWORD = "patch";
|
| -
|
| -
|
| - public static final String[] PSEUDO_KEYWORDS = {
|
| - ABSTRACT_KEYWORD,
|
| - AS_KEYWORD,
|
| - DYNAMIC_KEYWORD,
|
| - EXPORT_KEYWORD,
|
| - EXTERNAL_KEYWORD,
|
| - FACTORY_KEYWORD,
|
| - GETTER_KEYWORD,
|
| - IMPLEMENTS_KEYWORD,
|
| - IMPORT_KEYWORD,
|
| - LIBRARY_KEYWORD,
|
| - OPERATOR_KEYWORD,
|
| - PART_KEYWORD,
|
| - SETTER_KEYWORD,
|
| - STATIC_KEYWORD,
|
| - TYPEDEF_KEYWORD
|
| - };
|
| - public static final Set<String> PSEUDO_KEYWORDS_SET = ImmutableSet.copyOf(PSEUDO_KEYWORDS);
|
| -
|
| - public static final String[] RESERVED_WORDS = {
|
| - "break",
|
| - "case",
|
| - "catch",
|
| - "class",
|
| - "const",
|
| - "continue",
|
| - "default",
|
| - "do",
|
| - "else",
|
| - "extends",
|
| - "false",
|
| - "final",
|
| - "finally",
|
| - "for",
|
| - "if",
|
| - "in",
|
| - "is",
|
| - "new",
|
| - "null",
|
| - "return",
|
| - "rethrow",
|
| - "super",
|
| - "switch",
|
| - "this",
|
| - "throw",
|
| - "true",
|
| - "try",
|
| - "var",
|
| - "void",
|
| - "while"};
|
| - public static final Set<String> RESERVED_WORDS_SET = ImmutableSet.copyOf(RESERVED_WORDS);
|
| -
|
| - public DartParser(Source source,
|
| - String sourceCode,
|
| - boolean isDietParse,
|
| - Set<String> prefixes,
|
| - DartCompilerListener listener,
|
| - CompilerMetrics compilerMetrics) {
|
| - super(new DartParserCommentsHelper.CommentParserContext(source, sourceCode, listener, compilerMetrics));
|
| - this.source = source;
|
| - this.sourceCode = sourceCode;
|
| - this.isDietParse = isDietParse;
|
| - this.prefixes = prefixes;
|
| - this.allowNativeKeyword = source != null && PackageLibraryManager.isDartUri(source.getUri());
|
| - // check Unicode normalization
|
| - {
|
| - int indexOfDifference = StringUtils.indexOfDifference(sourceCode,
|
| - Normalizer.normalize(sourceCode, Normalizer.Form.NFC));
|
| - if (indexOfDifference != -1) {
|
| - DartCompilationError error = new DartCompilationError(source, new Location(
|
| - indexOfDifference, indexOfDifference + 1),
|
| - ParserErrorCode.INVALID_UNICODE_NORMALIZATION);
|
| - ctx.error(error);
|
| - }
|
| - }
|
| - }
|
| -
|
| - public static String read(Source source) throws IOException {
|
| - return read(source.getSourceReader());
|
| - }
|
| -
|
| - public static String read(Reader reader) throws IOException {
|
| - try {
|
| - return CharStreams.toString(reader);
|
| - } finally {
|
| - reader.close();
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * A flag indicating whether function expressions are allowed. See
|
| - * {@link #setAllowFunctionExpression(boolean)}.
|
| - */
|
| - private boolean allowFunctionExpression = true;
|
| -
|
| - /**
|
| - * 'break' (with no labels) and 'continue' stmts are not valid
|
| - * just anywhere, they must be inside a loop or a case stmt.
|
| - *
|
| - * A break with a label may be valid and is allowed through and
|
| - * checked in the resolver.
|
| - */
|
| - private boolean inLoopStatement = false;
|
| - private boolean inCaseStatement = false;
|
| -
|
| - /**
|
| - * Set the {@link #allowFunctionExpression} flag indicating whether function expressions are
|
| - * allowed, returning the old value. This is required to avoid ambiguity in a few places in the
|
| - * grammar.
|
| - *
|
| - * @param allow true if function expressions are allowed, false if not
|
| - * @return previous value of the flag, which should be restored
|
| - */
|
| - private boolean setAllowFunctionExpression(boolean allow) {
|
| - boolean old = allowFunctionExpression;
|
| - allowFunctionExpression = allow;
|
| - return old;
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * compilationUnit
|
| - * : libraryDeclaration? topLevelDefinition* EOF
|
| - * ;
|
| - *
|
| - * libraryDeclaration
|
| - * : libraryDirective? importDirective* sourceDirective* resourceDirective* nativeDirective*
|
| - *
|
| - * topLevelDefinition
|
| - * : classDefinition
|
| - * | interfaceDefinition
|
| - * | functionTypeAlias
|
| - * | methodOrConstructorDeclaration functionStatementBody
|
| - * | type? getOrSet identifier formalParameterList functionStatementBody
|
| - * | CONST type? staticConstDeclarationList ';'
|
| - * | variableDeclaration ';'
|
| - * ;
|
| - * </pre>
|
| - */
|
| - @Terminals(tokens={Token.EOS, Token.CLASS, Token.LIBRARY, Token.IMPORT, Token.SOURCE,
|
| - Token.RESOURCE, Token.NATIVE})
|
| - public DartUnit parseUnit() {
|
| - DartSource dartSource = (DartSource) source;
|
| -
|
| - errorCount = 0;
|
| -
|
| - try {
|
| - beginCompilationUnit();
|
| - ctx.unitAboutToCompile(dartSource, isDietParse);
|
| - DartUnit unit = new DartUnit(dartSource, isDietParse);
|
| - List<DartAnnotation> metadata = parseMetadata();
|
| -
|
| - // parse any directives at the beginning of the source
|
| - metadata = parseDirectives(unit, metadata);
|
| -
|
| - while (!EOS()) {
|
| - DartNodeWithMetadata node = null;
|
| - beginTopLevelElement();
|
| - isParsingClass = isParsingInterface = false;
|
| - // Check for ABSTRACT_KEYWORD.
|
| - isTopLevelAbstract = false;
|
| - topLevelAbstractModifierPosition = 0;
|
| - if (isBuiltInSpecial() && optionalPseudoKeyword(ABSTRACT_KEYWORD)) {
|
| - isTopLevelAbstract = true;
|
| - topLevelAbstractModifierPosition = position();
|
| - }
|
| - // skip "patch" before "class"
|
| - if (peek(1) == Token.CLASS && optionalPseudoKeyword(PATCH_KEYWORD)) {
|
| - }
|
| - // Parse top level element.
|
| - if (optional(Token.CLASS)) {
|
| - isParsingClass = true;
|
| - node = done(parseClass());
|
| - } else if (peekPseudoKeyword(0, INTERFACE_KEYWORD) && peek(1).equals(Token.IDENTIFIER)) {
|
| - consume(Token.IDENTIFIER);
|
| - isParsingInterface = true;
|
| - reportError(position(), ParserErrorCode.DEPRECATED_INTERFACE);
|
| - node = done(parseClass());
|
| - } else if (peekPseudoKeyword(0, TYPEDEF_KEYWORD)
|
| - && (peek(1).equals(Token.IDENTIFIER) || peek(1).equals(Token.VOID))) {
|
| - consume(Token.IDENTIFIER);
|
| - node = done(parseTypeAlias());
|
| - } else if (looksLikeDirective()) {
|
| - reportErrorWithoutAdvancing(ParserErrorCode.DIRECTIVE_OUT_OF_ORDER);
|
| - metadata = parseDirectives(unit, metadata);
|
| - } else {
|
| - node = done(parseFieldOrMethod(false));
|
| - }
|
| - // Parsing was successful, add node.
|
| - if (node != null) {
|
| - unit.getTopLevelNodes().add(node);
|
| - setMetadata(node, metadata);
|
| - metadata = parseMetadata();
|
| - // Only "class" can be top-level abstract element.
|
| - if (isTopLevelAbstract && !isParsingClass) {
|
| - int abstractPositionEnd = topLevelAbstractModifierPosition + ABSTRACT_KEYWORD.length();
|
| - Location location = new Location(topLevelAbstractModifierPosition, abstractPositionEnd);
|
| - reportError(new DartCompilationError(source, location,
|
| - ParserErrorCode.ABSTRACT_TOP_LEVEL_ELEMENT));
|
| - }
|
| - }
|
| - }
|
| - expect(Token.EOS);
|
| - // add comments
|
| - {
|
| - List<int[]> commentLocs = ((DartParserCommentsHelper.CommentParserContext) ctx).getCommentLocs();
|
| - DartParserCommentsHelper.addComments(unit, source, sourceCode, commentLocs);
|
| - }
|
| - // done
|
| - unit.setHasParseErrors(errorCount != 0);
|
| - return done(unit);
|
| - } catch (StringInterpolationParseError exception) {
|
| - throw new InternalCompilerException("Failed to parse " + source.getUri(), exception);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Set the metadata associated with the given node to the given annotations.
|
| - *
|
| - * @param node the node with which the metadata is to be associated
|
| - * @param metadata the metadata to be associated with the node
|
| - */
|
| - private void setMetadata(DartNodeWithMetadata node, List<DartAnnotation> annotations) {
|
| - if (node instanceof DartFieldDefinition) {
|
| - DartFieldDefinition fieldDefinition = (DartFieldDefinition) node;
|
| - List<DartField> fields = fieldDefinition.getFields();
|
| - for (DartField field : fields) {
|
| - setMetadata(field, annotations);
|
| - }
|
| - return;
|
| - }
|
| - if (annotations != null && !annotations.isEmpty()) {
|
| - node.setMetadata(annotations);
|
| - if (node instanceof HasObsoleteMetadata) {
|
| - HasObsoleteMetadata declaration = (HasObsoleteMetadata) node;
|
| - for (int i = 0, size = annotations.size(); i < size; i++) {
|
| - DartAnnotation annotation = annotations.get(i);
|
| - DartExpression nameNode = annotation.getName();
|
| - if (nameNode instanceof DartIdentifier) {
|
| - String name = ((DartIdentifier) nameNode).getName();
|
| - if (name.equals("deprecated")) {
|
| - declaration.setObsoleteMetadata(declaration.getObsoleteMetadata().makeDeprecated());
|
| - } else if (name.equals("override")) {
|
| - declaration.setObsoleteMetadata(declaration.getObsoleteMetadata().makeOverride());
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - private boolean looksLikeDirective() {
|
| - if (!isBuiltInSpecial()) {
|
| - return false;
|
| - }
|
| - switch(peek(0)) {
|
| - case LIBRARY:
|
| - case IMPORT:
|
| - case SOURCE:
|
| - case RESOURCE:
|
| - case NATIVE:
|
| - return true;
|
| - }
|
| - return peekPseudoKeyword(0, LIBRARY_KEYWORD) || peekPseudoKeyword(0, IMPORT_KEYWORD) || peekPseudoKeyword(0, PART_KEYWORD);
|
| - }
|
| -
|
| - /**
|
| - * 'interface' and 'typedef' are valid to use as names of fields and methods, so you can't
|
| - * just blindly recover when you see them in any context. This does a further test to make
|
| - * sure they are followed by another identifier. This would be illegal as a field or method
|
| - * definition, as you cannot use 'interface' or 'typedef' as a type name.
|
| - */
|
| - private boolean looksLikeTopLevelKeyword() {
|
| - if (peek(0).equals(Token.CLASS)) {
|
| - return true;
|
| - }
|
| - if (peekPseudoKeyword(0, INTERFACE_KEYWORD)
|
| - && peek(1).equals(Token.IDENTIFIER)) {
|
| - return true;
|
| - } else if (peekPseudoKeyword(0, TYPEDEF_KEYWORD)
|
| - && (peek(1).equals(Token.IDENTIFIER) || peek(1).equals(Token.VOID))) {
|
| - return true;
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - /**
|
| - * A version of the parser which only parses the directives of a library.
|
| - *
|
| - * TODO(jbrosenberg): consider parsing the whole file here, in order to avoid
|
| - * duplicate work. Probably requires removing use of LibraryUnit's, etc.
|
| - * Also, this minimal parse does have benefit in the incremental compilation
|
| - * case.
|
| - */
|
| - @SuppressWarnings("deprecation")
|
| - public LibraryUnit preProcessLibraryDirectives(LibrarySource source) {
|
| - beginCompilationUnit();
|
| - LibraryUnit libUnit = new LibraryUnit(source);
|
| - parseMetadata();
|
| - if (peekPseudoKeyword(0, LIBRARY_KEYWORD)) {
|
| - DartLibraryDirective libraryDirective = parseLibraryDirective();
|
| - libUnit.setName(libraryDirective.getLibraryName());
|
| - parseMetadata();
|
| - }
|
| - while (peekPseudoKeyword(0, IMPORT_KEYWORD) || peekPseudoKeyword(0, EXPORT_KEYWORD)) {
|
| - if (peekPseudoKeyword(0, IMPORT_KEYWORD)) {
|
| - DartImportDirective importDirective = parseImportDirective();
|
| - LibraryNode importPath = new LibraryNode(importDirective);
|
| - importPath.setSourceInfo(importDirective.getSourceInfo());
|
| - libUnit.addImportPath(importPath);
|
| - }
|
| - if (peekPseudoKeyword(0, EXPORT_KEYWORD)) {
|
| - DartExportDirective exportDirective = parseExportDirective();
|
| - LibraryNode importPath = new LibraryNode(exportDirective);
|
| - importPath.setSourceInfo(exportDirective.getSourceInfo());
|
| - libUnit.addExportPath(importPath);
|
| - }
|
| - parseMetadata();
|
| - }
|
| - while (peekPseudoKeyword(0, PART_KEYWORD)) {
|
| - if (peekPseudoKeyword(1, OF_KEYWORD)) {
|
| - parsePartOfDirective();
|
| - } else {
|
| - DartSourceDirective sourceDirective = parsePartDirective();
|
| - LibraryNode sourcePath = new LibraryNode(sourceDirective.getSourceUri().getValue());
|
| - sourcePath.setSourceInfo(sourceDirective.getSourceInfo());
|
| - libUnit.addSourcePath(sourcePath);
|
| - }
|
| - parseMetadata();
|
| - }
|
| - //
|
| - // The code below is obsolete. We do not make any effort to find duplications between the old
|
| - // and the new syntax because support for the old syntax will be removed very soon.
|
| - //
|
| - if (peek(0) == Token.LIBRARY) {
|
| - beginLibraryDirective();
|
| - DartLibraryDirective libDirective = done(parseObsoleteLibraryDirective());
|
| - libUnit.setName(libDirective.getLibraryName());
|
| - parseMetadata();
|
| - }
|
| - while (peek(0) == Token.IMPORT) {
|
| - beginImportDirective();
|
| - DartImportDirective importDirective = done(parseObsoleteImportDirective());
|
| - LibraryNode importPath;
|
| - if (importDirective.getOldPrefix() != null) {
|
| - importPath =
|
| - new LibraryNode(importDirective);
|
| - } else {
|
| - importPath = new LibraryNode(importDirective.getLibraryUri().getValue());
|
| - }
|
| - importPath.setSourceInfo(importDirective.getSourceInfo());
|
| - libUnit.addImportPath(importPath);
|
| - parseMetadata();
|
| - }
|
| - while (peek(0) == Token.SOURCE) {
|
| - beginSourceDirective();
|
| - DartSourceDirective sourceDirective = done(parseSourceDirective());
|
| - LibraryNode sourcePath = new LibraryNode(sourceDirective.getSourceUri().getValue());
|
| - sourcePath.setSourceInfo(sourceDirective.getSourceInfo());
|
| - libUnit.addSourcePath(sourcePath);
|
| - parseMetadata();
|
| - }
|
| - while (peek(0) == Token.RESOURCE) {
|
| - parseResourceDirective();
|
| - parseMetadata();
|
| - }
|
| - while (peek(0) == Token.NATIVE) {
|
| - beginNativeDirective();
|
| - DartNativeDirective nativeDirective = done(parseNativeDirective());
|
| - LibraryNode nativePath = new LibraryNode(nativeDirective.getNativeUri().getValue());
|
| - nativePath.setSourceInfo(nativeDirective.getSourceInfo());
|
| - libUnit.addNativePath(nativePath);
|
| - parseMetadata();
|
| - }
|
| -
|
| - // add ourselves to the list of sources, so inline dart code will be parsed
|
| - libUnit.addSourcePath(libUnit.getSelfSourcePath());
|
| - return done(libUnit);
|
| - }
|
| -
|
| - private List<DartAnnotation> parseDirectives(DartUnit unit, List<DartAnnotation> metadata) {
|
| - boolean hasLibraryDirective = false;
|
| - if (peekPseudoKeyword(0, LIBRARY_KEYWORD)) {
|
| - DartLibraryDirective libraryDirective = parseLibraryDirective();
|
| - for (DartDirective directive : unit.getDirectives()) {
|
| - if (directive instanceof DartLibraryDirective) {
|
| - reportError(position(), ParserErrorCode.ONLY_ONE_LIBRARY_DIRECTIVE);
|
| - break;
|
| - }
|
| - }
|
| - unit.getDirectives().add(libraryDirective);
|
| - hasLibraryDirective = true;
|
| - setMetadata(libraryDirective, metadata);
|
| - metadata = parseMetadata();
|
| - }
|
| - while (peekPseudoKeyword(0, IMPORT_KEYWORD) || peekPseudoKeyword(0, EXPORT_KEYWORD)) {
|
| - if (peekPseudoKeyword(0, IMPORT_KEYWORD)) {
|
| - DartImportDirective importDirective = parseImportDirective();
|
| - unit.getDirectives().add(importDirective);
|
| - setMetadata(importDirective, metadata);
|
| - metadata = parseMetadata();
|
| - } else {
|
| - DartExportDirective exportDirective = parseExportDirective();
|
| - unit.getDirectives().add(exportDirective);
|
| - if (!hasLibraryDirective) {
|
| - reportError(exportDirective, ParserErrorCode.EXPORT_WITHOUT_LIBRARY_DIRECTIVE);
|
| - }
|
| - setMetadata(exportDirective, metadata);
|
| - metadata = parseMetadata();
|
| - }
|
| - }
|
| - while (peekPseudoKeyword(0, PART_KEYWORD)) {
|
| - if (peekPseudoKeyword(1, OF_KEYWORD)) {
|
| - DartPartOfDirective partOfDirective = parsePartOfDirective();
|
| - unit.getDirectives().add(partOfDirective);
|
| - setMetadata(partOfDirective, metadata);
|
| - metadata = parseMetadata();
|
| - } else {
|
| - DartSourceDirective partDirective = parsePartDirective();
|
| - unit.getDirectives().add(partDirective);
|
| - setMetadata(partDirective, metadata);
|
| - metadata = parseMetadata();
|
| - }
|
| - }
|
| - //
|
| - // The code below is obsolete. We do not make any effort to find duplications between the old
|
| - // and the new syntax because support for the old syntax will be removed very soon.
|
| - //
|
| - if (peek(0) == Token.LIBRARY) {
|
| - beginLibraryDirective();
|
| - DartLibraryDirective libraryDirective = parseObsoleteLibraryDirective();
|
| - for (DartDirective directive : unit.getDirectives()) {
|
| - if (directive instanceof DartLibraryDirective) {
|
| - reportError(position(), ParserErrorCode.ONLY_ONE_LIBRARY_DIRECTIVE);
|
| - break;
|
| - }
|
| - }
|
| - unit.getDirectives().add(libraryDirective);
|
| - done(libraryDirective);
|
| - setMetadata(libraryDirective, metadata);
|
| - metadata = parseMetadata();
|
| - }
|
| - while (peek(0) == Token.IMPORT) {
|
| - beginImportDirective();
|
| - DartImportDirective importDirective = parseObsoleteImportDirective();
|
| - unit.getDirectives().add(done(importDirective));
|
| - setMetadata(importDirective, metadata);
|
| - metadata = parseMetadata();
|
| - }
|
| - while (peek(0) == Token.SOURCE) {
|
| - beginSourceDirective();
|
| - DartSourceDirective sourceDirective = parseSourceDirective();
|
| - unit.getDirectives().add(done(sourceDirective));
|
| - setMetadata(sourceDirective, metadata);
|
| - metadata = parseMetadata();
|
| - }
|
| - while (peek(0) == Token.RESOURCE) {
|
| - parseResourceDirective();
|
| - }
|
| - while (peek(0) == Token.NATIVE) {
|
| - beginNativeDirective();
|
| - DartNativeDirective nativeDirective = parseNativeDirective();
|
| - unit.getDirectives().add(done(nativeDirective));
|
| - setMetadata(nativeDirective, metadata);
|
| - metadata = parseMetadata();
|
| - }
|
| - return metadata;
|
| - }
|
| -
|
| - private DartLibraryDirective parseLibraryDirective() {
|
| - beginLibraryDirective();
|
| - next(); // "library"
|
| - DartExpression libraryName = parseLibraryName();
|
| - expect(Token.SEMICOLON);
|
| - return done(new DartLibraryDirective(libraryName));
|
| - }
|
| -
|
| - private DartExpression parseLibraryName() {
|
| - beginQualifiedIdentifier();
|
| - DartExpression libraryName = parseIdentifier();
|
| - while (optional(Token.PERIOD)) {
|
| - beginQualifiedIdentifier();
|
| - DartIdentifier identifier = parseIdentifier();
|
| - libraryName = done(new DartPropertyAccess(libraryName, identifier));
|
| - }
|
| - return done(libraryName);
|
| - }
|
| -
|
| - private DartLibraryDirective parseObsoleteLibraryDirective() {
|
| - expect(Token.LIBRARY);
|
| - reportDeprecatedError(position(), ParserErrorCode.DEPRECATED_LIBRARY_DIRECTIVE);
|
| - expect(Token.LPAREN);
|
| - beginLiteral();
|
| - expect(Token.STRING);
|
| - DartStringLiteral libname = done(DartStringLiteral.get(ctx.getTokenString()));
|
| - expectCloseParen();
|
| - expect(Token.SEMICOLON);
|
| - return new DartLibraryDirective(libname);
|
| - }
|
| -
|
| - protected DartExportDirective parseExportDirective() {
|
| - beginExportDirective();
|
| - next(); // "export"
|
| - DartStringLiteral libUri = parseUri();
|
| -
|
| - List<ImportCombinator> combinators = new ArrayList<ImportCombinator>();
|
| - while (peekPseudoKeyword(0, HIDE_KEYWORD) || peekPseudoKeyword(0, SHOW_KEYWORD)) {
|
| - beginImportCombinator();
|
| - if (optionalPseudoKeyword(HIDE_KEYWORD)) {
|
| - List<DartIdentifier> hiddenNames = parseIdentifierList();
|
| - combinators.add(done(new ImportHideCombinator(hiddenNames)));
|
| - } else if (optionalPseudoKeyword(SHOW_KEYWORD)) {
|
| - List<DartIdentifier> shownNames = parseIdentifierList();
|
| - combinators.add(done(new ImportShowCombinator(shownNames)));
|
| - }
|
| - }
|
| -
|
| - if (!optional(Token.SEMICOLON)) {
|
| - // If there is no semicolon, then we probably don't want to consume the next token. It might
|
| - // make sense to advance to the next valid token for a directive or top-level declaration, but
|
| - // our recovery mechanism isn't quite sophisticated enough for that.
|
| - reportUnexpectedToken(position(), Token.SEMICOLON, peek(0));
|
| - }
|
| - return done(new DartExportDirective(libUri, combinators));
|
| - }
|
| -
|
| - protected DartImportDirective parseImportDirective() {
|
| - beginImportDirective();
|
| - next(); // "import"
|
| - DartStringLiteral libUri = parseUri();
|
| - // allow "native" if we have "dart-ext:" import
|
| - if (StringUtils.startsWith(libUri.getValue(), "dart-ext:")) {
|
| - allowNativeKeyword = true;
|
| - }
|
| -
|
| - DartIdentifier prefix = null;
|
| - if (peek(0) == Token.IDENTIFIER && "as".equals(ctx.peekTokenString(0))) {
|
| - ctx.advance();
|
| - prefix = parseIdentifier();
|
| - if (prefix instanceof DartSyntheticErrorIdentifier) {
|
| - if (peekPseudoKeyword(1, HIDE_KEYWORD) || peekPseudoKeyword(1, SHOW_KEYWORD)
|
| - || peek(1) == Token.BIT_AND || peek(1) == Token.COLON) {
|
| - next();
|
| - }
|
| - }
|
| - }
|
| -
|
| - List<ImportCombinator> combinators = new ArrayList<ImportCombinator>();
|
| - while (peekPseudoKeyword(0, HIDE_KEYWORD) || peekPseudoKeyword(0, SHOW_KEYWORD)) {
|
| - if (optionalPseudoKeyword(HIDE_KEYWORD)) {
|
| - List<DartIdentifier> hiddenNames = parseIdentifierList();
|
| - combinators.add(new ImportHideCombinator(hiddenNames));
|
| - } else if (optionalPseudoKeyword(SHOW_KEYWORD)) {
|
| - List<DartIdentifier> shownNames = parseIdentifierList();
|
| - combinators.add(new ImportShowCombinator(shownNames));
|
| - }
|
| - }
|
| -
|
| - if (!optional(Token.SEMICOLON)) {
|
| - // If there is no semicolon, then we probably don't want to consume the next token. It might
|
| - // make sense to advance to the next valid token for a directive or top-level declaration, but
|
| - // our recovery mechanism isn't quite sophisticated enough for that.
|
| - reportUnexpectedToken(position(), Token.SEMICOLON, peek(0));
|
| - }
|
| - return done(new DartImportDirective(libUri, prefix, combinators));
|
| - }
|
| -
|
| - /**
|
| - * Parse a comma-separated list of identifiers.
|
| - *
|
| - * @return the identifiers that were parsed
|
| - */
|
| - private List<DartIdentifier> parseIdentifierList() {
|
| - ArrayList<DartIdentifier> identifiers = new ArrayList<DartIdentifier>();
|
| - identifiers.add(parseIdentifier());
|
| - while (optional(Token.COMMA)) {
|
| - identifiers.add(parseIdentifier());
|
| - }
|
| - return identifiers;
|
| - }
|
| -
|
| - protected DartImportDirective parseObsoleteImportDirective() {
|
| - expect(Token.IMPORT);
|
| - reportDeprecatedError(position(), ParserErrorCode.DEPRECATED_IMPORT_DIRECTIVE);
|
| - expect(Token.LPAREN);
|
| -
|
| - DartStringLiteral libUri = parseUri();
|
| -
|
| - // allow "native" if we have "dart-ext:" import
|
| - if (StringUtils.startsWith(libUri.getValue(), "dart-ext:")) {
|
| - allowNativeKeyword = true;
|
| - }
|
| -
|
| - DartBooleanLiteral export = null;
|
| - List<ImportCombinator> combinators = new ArrayList<ImportCombinator>();
|
| - DartStringLiteral prefix = null;
|
| - if (optional(Token.COMMA)) {
|
| - if (optionalPseudoKeyword(PREFIX_KEYWORD)) {
|
| - expect(Token.COLON);
|
| - beginLiteral();
|
| - expect(Token.STRING);
|
| - String id = ctx.getTokenString();
|
| - // The specification requires the value of this string be a valid identifier
|
| - if(id == null || !id.matches("[_a-zA-Z]([_A-Za-z0-9]*)")) {
|
| - reportError(position(), ParserErrorCode.EXPECTED_PREFIX_IDENTIFIER);
|
| - }
|
| - prefix = done(DartStringLiteral.get(ctx.getTokenString()));
|
| - } else {
|
| - reportError(position(), ParserErrorCode.EXPECTED_PREFIX_KEYWORD);
|
| - }
|
| - }
|
| - expectCloseParen();
|
| - expect(Token.SEMICOLON);
|
| - return new DartImportDirective(libUri, export, combinators, prefix);
|
| - }
|
| -
|
| - private DartSourceDirective parsePartDirective() {
|
| - beginPartDirective();
|
| - next(); // "part"
|
| - beginLiteral();
|
| - expect(Token.STRING);
|
| - DartStringLiteral partUri = done(DartStringLiteral.get(ctx.getTokenString()));
|
| - expect(Token.SEMICOLON);
|
| - return done(new DartSourceDirective(partUri));
|
| - }
|
| -
|
| - private DartSourceDirective parseSourceDirective() {
|
| - expect(Token.SOURCE);
|
| - reportDeprecatedError(position(), ParserErrorCode.DEPRECATED_SOURCE_DIRECTIVE);
|
| - expect(Token.LPAREN);
|
| - DartStringLiteral sourceUri = parseUri();
|
| - expectCloseParen();
|
| - expect(Token.SEMICOLON);
|
| - return new DartSourceDirective(sourceUri);
|
| - }
|
| -
|
| - private DartPartOfDirective parsePartOfDirective() {
|
| - beginPartOfDirective();
|
| - next(); // "part"
|
| - next(); // "of"
|
| - int ofOffset= position();
|
| - DartExpression libraryName = parseLibraryName();
|
| - expect(Token.SEMICOLON);
|
| - return done(new DartPartOfDirective(ofOffset, libraryName));
|
| - }
|
| -
|
| - private void parseResourceDirective() {
|
| - expect(Token.RESOURCE);
|
| - reportError(position(), ParserErrorCode.DEPRECATED_RESOURCE_DIRECTIVE);
|
| - expect(Token.LPAREN);
|
| - @SuppressWarnings("unused")
|
| - DartStringLiteral sourceUri = parseUri();
|
| - expectCloseParen();
|
| - expect(Token.SEMICOLON);
|
| - }
|
| -
|
| - private DartNativeDirective parseNativeDirective() {
|
| - expect(Token.NATIVE);
|
| - expect(Token.LPAREN);
|
| - DartStringLiteral nativeUri = parseUri();
|
| - expect(Token.RPAREN);
|
| - expect(Token.SEMICOLON);
|
| - return new DartNativeDirective(nativeUri);
|
| - }
|
| -
|
| - private List<DartAnnotation> parseMetadata() {
|
| - List<DartAnnotation> metadata = new ArrayList<DartAnnotation>();
|
| - while (match(Token.AT)) {
|
| - beginMetadata();
|
| - next();
|
| - beginQualifiedIdentifier();
|
| - DartExpression name = parseQualified(true);
|
| - if (optional(Token.PERIOD)) {
|
| - name = new DartPropertyAccess(name, parseIdentifier());
|
| - }
|
| - done(name);
|
| - List<DartExpression> arguments = null;
|
| - if (match(Token.LPAREN)) {
|
| - arguments = parseArguments();
|
| - }
|
| - metadata.add(done(new DartAnnotation(name, arguments)));
|
| - }
|
| - return metadata;
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * typeParameter
|
| - * : identifier (EXTENDS type)?
|
| - * ;
|
| - *
|
| - * typeParameters
|
| - * : '<' typeParameter (',' typeParameter)* '>'
|
| - * ;
|
| - * </pre>
|
| - */
|
| - @Terminals(tokens={Token.GT, Token.COMMA})
|
| - private List<DartTypeParameter> parseTypeParameters() {
|
| - List<DartTypeParameter> types = new ArrayList<DartTypeParameter>();
|
| - expect(Token.LT);
|
| - do {
|
| - DartTypeParameter typeParameter = parseTypeParameter();
|
| - types.add(typeParameter);
|
| -
|
| - } while (optional(Token.COMMA));
|
| - expect(Token.GT);
|
| - return types;
|
| - }
|
| -
|
| - /**
|
| - * Parses single {@link DartTypeParameter} for {@link #parseTypeParameters()}.
|
| - */
|
| - private DartTypeParameter parseTypeParameter() {
|
| - beginTypeParameter();
|
| - List<DartAnnotation> metadata = parseMetadata();
|
| - DartIdentifier name = parseIdentifier();
|
| - if (PSEUDO_KEYWORDS_SET.contains(name.getName())) {
|
| - reportError(name, ParserErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_VARIABLE_NAME);
|
| - }
|
| - // Try to parse bound.
|
| - DartTypeNode bound = null;
|
| - if (peek(0) != Token.EOS && peek(0) != Token.COMMA && peek(0) != Token.GT) {
|
| - if (optional(Token.EXTENDS)) {
|
| - // OK, this is EXTENDS, parse type.
|
| - bound = parseTypeAnnotation();
|
| - } else if (looksLikeTopLevelKeyword()) {
|
| - return done(new DartTypeParameter(name, bound));
|
| - } else if (peek(0) == Token.IDENTIFIER && (peek(1) == Token.COMMA || peek(1) == Token.GT)) {
|
| - // <X exte{cursor}>
|
| - // User tries to type "extends", but it is not finished yet.
|
| - // Report problem and try to continue.
|
| - next();
|
| - reportError(position(), ParserErrorCode.EXPECTED_EXTENDS);
|
| - } else if (peek(0) == Token.IDENTIFIER
|
| - && peek(1) == Token.IDENTIFIER
|
| - && (peek(2) == Token.COMMA || peek(2) == Token.GT)) {
|
| - // <X somethingLikeExtends Type>
|
| - // User mistyped word "extends" or it is not finished yet.
|
| - // Report problem and try to continue.
|
| - next();
|
| - reportError(position(), ParserErrorCode.EXPECTED_EXTENDS);
|
| - bound = parseTypeAnnotation();
|
| - } else {
|
| - // Something else, restart parsing from next top level element.
|
| - next();
|
| - reportError(position(), ParserErrorCode.EXPECTED_EXTENDS);
|
| - }
|
| - }
|
| - // Ready to create DartTypeParameter.
|
| - DartTypeParameter parameter = new DartTypeParameter(name, bound);
|
| - parameter.setMetadata(metadata);
|
| - return done(parameter);
|
| - }
|
| -
|
| - private List<DartTypeParameter> parseTypeParametersOpt() {
|
| - return (peek(0) == Token.LT)
|
| - ? parseTypeParameters()
|
| - : Collections.<DartTypeParameter>emptyList();
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * classDefinition
|
| - * : CLASS identifier typeParameters? superclass? interfaces?
|
| - * '{' classMemberDefinition* '}'
|
| - * ;
|
| - *
|
| - * superclass
|
| - * : EXTENDS type
|
| - * ;
|
| - *
|
| - * interfaces
|
| - * : IMPLEMENTS typeList
|
| - * ;
|
| - *
|
| - * superinterfaces
|
| - * : EXTENDS typeList
|
| - * ;
|
| - *
|
| - * classMemberDefinition
|
| - * : declaration ';'
|
| - * | methodDeclaration blockOrNative
|
| - *
|
| - * interfaceDefinition
|
| - * : INTERFACE identifier typeParameters? superinterfaces?
|
| - * (DEFAULT type)? '{' (interfaceMemberDefinition)* '}'
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private DartDeclaration<?> parseClass() {
|
| - beginClassBody();
|
| -
|
| - int tokenOffset = ctx.getTokenLocation().getBegin();
|
| - int tokenLength = ctx.getTokenLocation().getEnd() - tokenOffset;
|
| -
|
| - // Parse modifiers.
|
| - Modifiers modifiers = Modifiers.NONE;
|
| - if (isTopLevelAbstract) {
|
| - modifiers = modifiers.makeAbstract();
|
| - }
|
| -
|
| - DartIdentifier name = parseIdentifier();
|
| - if (name.getName().equals("")) {
|
| - // something went horribly wrong.
|
| - if (peek(0).equals(Token.LBRACE)) {
|
| - parseBlock();
|
| - }
|
| - return done(null);
|
| - }
|
| - if (PSEUDO_KEYWORDS_SET.contains(name.getName())) {
|
| - reportError(name, ParserErrorCode.BUILT_IN_IDENTIFIER_AS_TYPE_NAME);
|
| - }
|
| - List<DartTypeParameter> typeParameters = parseTypeParametersOpt();
|
| -
|
| - // Parse the extends and implements clauses.
|
| - DartTypeNode superType = null;
|
| - int withOffset = -1;
|
| - int implementsOffset = -1;
|
| - List<DartTypeNode> mixins = null;
|
| - List<DartTypeNode> interfaces = null;
|
| - if (isParsingInterface) {
|
| - if (optional(Token.EXTENDS)) {
|
| - interfaces = parseTypeAnnotationList();
|
| - }
|
| - } else {
|
| - boolean foundClause = true;
|
| - while (foundClause) {
|
| - if (optional(Token.EXTENDS)) {
|
| - if (mixins != null) {
|
| - reportErrorAtPosition(withOffset, withOffset + "with".length(),
|
| - ParserErrorCode.WITH_BEFORE_EXTENDS);
|
| - }
|
| - if (interfaces != null) {
|
| - reportErrorAtPosition(implementsOffset, implementsOffset + "implements".length(),
|
| - ParserErrorCode.IMPLEMENTS_BEFORE_EXTENDS);
|
| - }
|
| - if (superType == null) {
|
| - superType = parseTypeAnnotation();
|
| - } else {
|
| - reportError(position(), ParserErrorCode.MULTIPLE_EXTENDS_CLAUSES);
|
| - parseTypeAnnotation();
|
| - }
|
| - } else if (optional(Token.WITH)) {
|
| - if (mixins == null) {
|
| - withOffset = position();
|
| - mixins = parseTypeAnnotationList();
|
| - if (interfaces != null) {
|
| - reportErrorAtPosition(implementsOffset, implementsOffset + "implements".length(),
|
| - ParserErrorCode.IMPLEMENTS_BEFORE_WITH);
|
| - }
|
| - } else {
|
| - reportError(position(), ParserErrorCode.MULTIPLE_WITH_CLAUSES);
|
| - parseTypeAnnotationList();
|
| - }
|
| - } else if (optionalPseudoKeyword(IMPLEMENTS_KEYWORD)) {
|
| - if (interfaces == null) {
|
| - implementsOffset = position();
|
| - interfaces = parseTypeAnnotationList();
|
| - } else {
|
| - reportError(position(), ParserErrorCode.MULTIPLE_IMPLEMENTS_CLAUSES);
|
| - parseTypeAnnotationList();
|
| - }
|
| - } else {
|
| - foundClause = false;
|
| - }
|
| - }
|
| - if (mixins != null && superType == null) {
|
| - reportErrorAtPosition(withOffset, withOffset + "with".length(),
|
| - ParserErrorCode.WITH_WITHOUT_EXTENDS);
|
| - }
|
| - }
|
| -
|
| - // Deal with factory clause for interfaces.
|
| - DartParameterizedTypeNode defaultClass = null;
|
| - int defaultTokenOffset = -1;
|
| - if (isParsingInterface &&
|
| - (optionalDeprecatedFactory() || optional(Token.DEFAULT))) {
|
| - defaultTokenOffset = position();
|
| - beginTypeAnnotation();
|
| - DartExpression qualified = parseQualified(false);
|
| - List<DartTypeParameter> defaultTypeParameters = parseTypeParametersOpt();
|
| - defaultClass = doneWithoutConsuming(new DartParameterizedTypeNode(qualified,
|
| - defaultTypeParameters));
|
| - }
|
| -
|
| - // Deal with native clause for classes.
|
| - DartStringLiteral nativeName = null;
|
| - if (optionalPseudoKeyword(NATIVE_KEYWORD)) {
|
| - if (isParsingInterface) {
|
| - reportError(position(), ParserErrorCode.NATIVE_ONLY_CLASS);
|
| - }
|
| - if (!allowNativeKeyword) {
|
| - reportError(position(), ParserErrorCode.NATIVE_ONLY_CORE_LIB);
|
| - }
|
| - beginLiteral();
|
| - if (expect(Token.STRING)) {
|
| - nativeName = done(DartStringLiteral.get(ctx.getTokenString()));
|
| - }
|
| - modifiers = modifiers.makeNative();
|
| - }
|
| -
|
| - // Parse the members.
|
| - int openBraceOffset = -1;
|
| - int closeBraceOffset = -1;
|
| - List<DartNode> members = new ArrayList<DartNode>();
|
| - if (optional(Token.LBRACE)) {
|
| - openBraceOffset = ctx.getTokenLocation().getBegin();
|
| - parseClassOrInterfaceBody(members);
|
| - expectCloseBrace(true);
|
| - closeBraceOffset = ctx.getTokenLocation().getBegin();
|
| - } else {
|
| - reportErrorWithoutAdvancing(ParserErrorCode.EXPECTED_CLASS_DECLARATION_LBRACE);
|
| - }
|
| -
|
| - if (isParsingInterface) {
|
| - return done(new DartClass(tokenOffset, tokenLength, name, null, superType, implementsOffset,
|
| - interfaces, mixins, defaultTokenOffset, openBraceOffset, closeBraceOffset, members,
|
| - typeParameters, defaultClass, true, Modifiers.NONE));
|
| - } else {
|
| - return done(new DartClass(tokenOffset, tokenLength, name, nativeName, superType,
|
| - implementsOffset, interfaces, mixins, defaultTokenOffset, openBraceOffset,
|
| - closeBraceOffset, members, typeParameters, null, false, modifiers));
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Helper for {@link #parseClass()}.
|
| - *
|
| - * classMemberDefinition*
|
| - */
|
| - @Terminals(tokens={Token.RBRACE, Token.SEMICOLON})
|
| - private void parseClassOrInterfaceBody(List<DartNode> members) {
|
| - while (!match(Token.RBRACE) && !EOS() && !looksLikeTopLevelKeyword()) {
|
| - List<DartAnnotation> metadata = parseMetadata();
|
| - DartNodeWithMetadata member = parseFieldOrMethod(true);
|
| - if (member != null) {
|
| - setMetadata(member, metadata);
|
| - members.add(member);
|
| - }
|
| - // Recover at a semicolon
|
| - if (optional(Token.SEMICOLON)) {
|
| - reportUnexpectedToken(position(), null, Token.SEMICOLON);
|
| - }
|
| - }
|
| - }
|
| -
|
| - private boolean optionalDeprecatedFactory() {
|
| - if (optionalPseudoKeyword(FACTORY_KEYWORD)) {
|
| - reportError(position(), ParserErrorCode.DEPRECATED_USE_OF_FACTORY_KEYWORD);
|
| - return true;
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - private List<DartTypeNode> parseTypeAnnotationList() {
|
| - List<DartTypeNode> result = new ArrayList<DartTypeNode>();
|
| - do {
|
| - result.add(parseTypeAnnotation());
|
| - } while (optional(Token.COMMA));
|
| - return result;
|
| - }
|
| -
|
| - /**
|
| - * Look ahead to detect if we are seeing ident [ TypeParameters ] "(".
|
| - * We need this lookahead to distinguish between the optional return type
|
| - * and the alias name of a function type alias.
|
| - * Token position remains unchanged.
|
| - *
|
| - * @return true if the next tokens should be parsed as a type
|
| - */
|
| - private boolean isFunctionTypeAliasName() {
|
| - beginFunctionTypeInterface();
|
| - try {
|
| - if (peek(0) == Token.IDENTIFIER && peek(1) == Token.LPAREN) {
|
| - return true;
|
| - }
|
| - if (peek(0) == Token.IDENTIFIER && peek(1) == Token.LT) {
|
| - consume(Token.IDENTIFIER);
|
| - // isTypeParameter leaves the position advanced if it matches
|
| - if (isTypeParameter() && peek(0) == Token.LPAREN) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - } finally {
|
| - rollback();
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Returns true if the current and next tokens can be parsed as type
|
| - * parameters. Current token position is not saved and restored.
|
| - */
|
| - private boolean isTypeParameter() {
|
| - if (peek(0) == Token.LT) {
|
| - // We are possibly looking at type parameters. Find closing ">".
|
| - consume(Token.LT);
|
| - int nestingLevel = 1;
|
| - while (nestingLevel > 0) {
|
| - switch (peek(0)) {
|
| - case LT:
|
| - nestingLevel++;
|
| - break;
|
| - case GT:
|
| - nestingLevel--;
|
| - break;
|
| - case SAR: // >>
|
| - nestingLevel -= 2;
|
| - break;
|
| - case COMMA:
|
| - case EXTENDS:
|
| - case IDENTIFIER:
|
| - break;
|
| - default:
|
| - // We are looking at something other than type parameters.
|
| - return false;
|
| - }
|
| - next();
|
| - if (nestingLevel < 0) {
|
| - return false;
|
| - }
|
| - }
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - /**
|
| - * Parse a type alias.
|
| - *
|
| - * <pre>
|
| - * typeAlias ::=
|
| - * 'typedef' typeAliasBody
|
| - *
|
| - * typeAliasBody ::=
|
| - * classTypeAlias
|
| - * | functionTypeAlias
|
| - *
|
| - * classTypeAlias ::=
|
| - * identifier typeParameters? '=' 'abstract'? mixinApplication
|
| - *
|
| - * mixinApplication ::=
|
| - * qualified withClause implementsClause? ';'
|
| - *
|
| - * functionTypeAlias ::=
|
| - * functionPrefix typeParameterList? formalParameterList ';'
|
| - *
|
| - * functionPrefix ::=
|
| - * returnType? name
|
| - * </pre>
|
| - *
|
| - * @return the type alias that was parsed
|
| - */
|
| - private DartNodeWithMetadata parseTypeAlias() {
|
| - if (match(Token.IDENTIFIER)) {
|
| - Token next = peek(1);
|
| - if (next == Token.LT) {
|
| - int offset = skipTypeArguments(1, new DepthCounter());
|
| - next = peek(offset);
|
| - if (next != null && next == Token.ASSIGN) {
|
| - return parseClassTypeAlias();
|
| - }
|
| - } else if (next == Token.ASSIGN) {
|
| - return parseClassTypeAlias();
|
| - }
|
| - }
|
| - return parseFunctionTypeAlias();
|
| - }
|
| -
|
| - /**
|
| - * Parse a class type alias.
|
| - *
|
| - * <pre>
|
| - * classTypeAlias ::=
|
| - * identifier typeParameters? '=' 'abstract'? mixinApplication
|
| - *
|
| - * mixinApplication ::=
|
| - * type withClause implementsClause? ';'
|
| - * </pre>
|
| - *
|
| - * @return the class type alias that was parsed
|
| - */
|
| - private DartClassTypeAlias parseClassTypeAlias() {
|
| - beginClassTypeInterface();
|
| -
|
| - DartIdentifier name = parseIdentifier();
|
| - if (PSEUDO_KEYWORDS_SET.contains(name.getName())) {
|
| - reportError(name, ParserErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME);
|
| - }
|
| - List<DartTypeParameter> typeParameters = parseTypeParametersOpt();
|
| -
|
| - expect(Token.ASSIGN);
|
| -
|
| - Modifiers modifiers = Modifiers.NONE;
|
| - if (optionalPseudoKeyword(ABSTRACT_KEYWORD)) {
|
| - modifiers = modifiers.makeAbstract();
|
| - }
|
| -
|
| - DartTypeNode superType = parseTypeAnnotation();
|
| -
|
| - List<DartTypeNode> mixins = null;
|
| - if (optional(Token.WITH)) {
|
| - mixins = parseTypeAnnotationList();
|
| - }
|
| -
|
| - List<DartTypeNode> interfaces = null;
|
| - if (optionalPseudoKeyword(IMPLEMENTS_KEYWORD)) {
|
| - interfaces = parseTypeAnnotationList();
|
| - }
|
| -
|
| - expect(Token.SEMICOLON);
|
| - return done(new DartClassTypeAlias(name, typeParameters, modifiers, superType, mixins,
|
| - interfaces));
|
| - }
|
| -
|
| - /**
|
| - * Parse a function type alias.
|
| - *
|
| - * <pre>
|
| - * functionTypeAlias ::=
|
| - * functionPrefix typeParameterList? formalParameterList ';'
|
| - *
|
| - * functionPrefix ::=
|
| - * returnType? name
|
| - * </pre>
|
| - *
|
| - * @return the function type alias that was parsed
|
| - */
|
| - private DartFunctionTypeAlias parseFunctionTypeAlias() {
|
| - beginFunctionTypeInterface();
|
| -
|
| - DartTypeNode returnType = null;
|
| - if (peek(0) == Token.VOID) {
|
| - returnType = parseVoidType();
|
| - } else if (!isFunctionTypeAliasName()) {
|
| - returnType = parseTypeAnnotation();
|
| - }
|
| -
|
| - DartIdentifier name = parseIdentifier();
|
| - if (PSEUDO_KEYWORDS_SET.contains(name.getName())) {
|
| - reportError(name, ParserErrorCode.BUILT_IN_IDENTIFIER_AS_TYPEDEF_NAME);
|
| - }
|
| -
|
| - List<DartTypeParameter> typeParameters = parseTypeParametersOpt();
|
| - FormalParameters params = parseFormalParameterList();
|
| - expect(Token.SEMICOLON);
|
| - validateNoDefaultParameterValues(
|
| - params.val,
|
| - ParserErrorCode.DEFAULT_VALUE_CAN_NOT_BE_SPECIFIED_IN_TYPEDEF);
|
| -
|
| - return done(new DartFunctionTypeAlias(name, returnType, params.val, typeParameters));
|
| - }
|
| -
|
| - /**
|
| - * Parse a field or method, which may be inside a class or at the top level.
|
| - *
|
| - * <pre>
|
| - * // This rule is organized in a way that may not be most readable, but
|
| - * // gives the best error messages.
|
| - * classMemberDefinition
|
| - * : declaration ';'
|
| - * | methodDeclaration bodyOrNative
|
| - * ;
|
| - *
|
| - * // Note: this syntax is not official, but used in dart_interpreter. It
|
| - * // is unlikely that Dart will support numbered natives.
|
| - * bodyOrNative
|
| - * : error=NATIVE (':' (STRING | RATIONAL_NUMBER))? ';'
|
| - * { legacy($error, "native not supported (yet)"); }
|
| - * | functionStatementBody
|
| - * ;
|
| - *
|
| - * // A method, operator, or constructor (which all should be followed by
|
| - * // a function body).
|
| - * methodDeclaration
|
| - * : factoryConstructorDeclaration
|
| - * | STATIC methodOrConstructorDeclaration
|
| - * | specialSignatureDefinition
|
| - * | methodOrConstructorDeclaration initializers?
|
| - * | namedConstructorDeclaration initializers?
|
| - * ;
|
| - *
|
| - *
|
| - * // An abstract method/operator, a field, or const constructor (which
|
| - * // all should be followed by a semicolon).
|
| - * declaration
|
| - * : constantConstructorDeclaration initializers?
|
| - * | ABSTRACT specialSignatureDefinition
|
| - * | ABSTRACT methodOrConstructorDeclaration
|
| - * | STATIC CONST type? staticConstDeclarationList
|
| - * | STATIC? variableDeclaration
|
| - * ;
|
| - *
|
| - * interfaceMemberDefinition
|
| - * : STATIC CONST type? initializedIdentifierList ';'
|
| - * | methodOrConstructorDeclaration ';'
|
| - * | constantConstructorDeclaration ';'
|
| - * | namedConstructorDeclaration ';'
|
| - * | specialSignatureDefinition ';'
|
| - * | variableDeclaration ';'
|
| - * ;
|
| - *
|
| - * variableDeclaration
|
| - * : constVarOrType identifierList
|
| - * ;
|
| - *
|
| - * methodOrConstructorDeclaration
|
| - * : typeOrFunction? identifier formalParameterList
|
| - * ;
|
| - *
|
| - * factoryConstructorDeclaration
|
| - * : FACTORY qualified ('.' identifier)? formalParameterList
|
| - * ;
|
| - *
|
| - * namedConstructorDeclaration
|
| - * : identifier typeArguments? '.' identifier formalParameterList
|
| - * ;
|
| - *
|
| - * constructorDeclaration
|
| - * : identifier typeArguments? formalParameterList
|
| - * | namedConstructorDeclaration
|
| - * ;
|
| - *
|
| - * constantConstructorDeclaration
|
| - * : CONST qualified formalParameterList
|
| - * ;
|
| - *
|
| - * specialSignatureDefinition
|
| - * : STATIC? type? getOrSet identifier formalParameterList
|
| - * | type? OPERATOR operator formalParameterList
|
| - * ;
|
| - *
|
| - * getOrSet
|
| - * : GET
|
| - * | SET
|
| - * ;
|
| - *
|
| - * operator
|
| - * : unaryOperator
|
| - * | binaryOperator
|
| - * | '[' ']' { "[]".equals($text) }?
|
| - * | '[' ']' '=' { "[]=".equals($text) }?
|
| - * | NEGATE
|
| - * | CALL
|
| - * ;
|
| - * </pre>
|
| - *
|
| - * @param allowStatic true if the static modifier is allowed
|
| - * @return a {@link DartNode} representing the grammar fragment above
|
| - */
|
| - @Terminals(tokens={Token.SEMICOLON})
|
| - private DartNodeWithMetadata parseFieldOrMethod(boolean allowStatic) {
|
| - beginClassMember();
|
| - Modifiers modifiers = Modifiers.NONE;
|
| - if (isBuiltInSpecial() && optionalPseudoKeyword(EXTERNAL_KEYWORD)) {
|
| - modifiers = modifiers.makeExternal();
|
| - }
|
| - if (isBuiltInSpecial() && optionalPseudoKeyword(STATIC_KEYWORD)) {
|
| - if (!allowStatic) {
|
| - reportError(position(), ParserErrorCode.TOP_LEVEL_CANNOT_BE_STATIC);
|
| - } else {
|
| - if (isParsingInterface
|
| - && peek(0) != Token.FINAL && peek(0) != Token.CONST) {
|
| - reportError(position(), ParserErrorCode.NON_FINAL_STATIC_MEMBER_IN_INTERFACE);
|
| - }
|
| - modifiers = modifiers.makeStatic();
|
| - }
|
| - }
|
| - if (isBuiltInSpecial() && optionalPseudoKeyword(ABSTRACT_KEYWORD)) {
|
| - if (modifiers.isStatic()) {
|
| - reportError(position(), ParserErrorCode.STATIC_MEMBERS_CANNOT_BE_ABSTRACT);
|
| - }
|
| - if (modifiers.isExternal()) {
|
| - reportError(position(), ParserErrorCode.EXTERNAL_ABSTRACT);
|
| - }
|
| - modifiers = modifiers.makeAbstract();
|
| - }
|
| - if (isBuiltInSpecial() && optionalPseudoKeyword(FACTORY_KEYWORD)) {
|
| - if (isParsingInterface) {
|
| - reportError(position(), ParserErrorCode.FACTORY_MEMBER_IN_INTERFACE);
|
| - }
|
| - if (modifiers.isStatic()) {
|
| - reportError(position(), ParserErrorCode.FACTORY_CANNOT_BE_STATIC);
|
| - }
|
| - if (modifiers.isAbstract()) {
|
| - reportError(position(), ParserErrorCode.FACTORY_CANNOT_BE_ABSTRACT);
|
| - }
|
| -
|
| - modifiers = modifiers.makeFactory();
|
| - }
|
| -
|
| - if (match(Token.VAR) || match(Token.FINAL)) {
|
| - if (modifiers.isAbstract()) {
|
| - reportError(position(), ParserErrorCode.DISALLOWED_ABSTRACT_KEYWORD);
|
| - }
|
| - if (modifiers.isFactory()) {
|
| - reportError(position(), ParserErrorCode.DISALLOWED_FACTORY_KEYWORD);
|
| - }
|
| - }
|
| -
|
| - // report "abstract" warning after all other checks to don't hide error with warning
|
| - // we ignore problems if there was already reported problem after given position
|
| - if (modifiers.isAbstract()) {
|
| - reportError(position(), ParserErrorCode.DEPRECATED_ABSTRACT_METHOD);
|
| - }
|
| -
|
| - if (modifiers.isFactory()) {
|
| - if (!isParsingClass) {
|
| - reportError(position(), ParserErrorCode.FACTORY_CANNOT_BE_TOP_LEVEL);
|
| - }
|
| - // Do parse factory.
|
| - DartMethodDefinition factoryNode = parseFactory(modifiers);
|
| - // If factory is not allowed, ensure that it is valid as method.
|
| - DartExpression actualName = factoryNode.getName();
|
| - if (!allowStatic && !(actualName instanceof DartIdentifier)) {
|
| - DartExpression replacementName = new DartIdentifier(actualName.toString());
|
| - factoryNode.setName(replacementName);
|
| - }
|
| - // Done.
|
| - return done(factoryNode);
|
| - }
|
| -
|
| - final DartNodeWithMetadata member;
|
| -
|
| - switch (peek(0)) {
|
| - case VAR: {
|
| - consume(Token.VAR);
|
| - // Check for malformed method starting with 'var' : var ^ foo() { }
|
| - if (peek(0).equals(Token.IDENTIFIER) && looksLikeMethodOrAccessorDefinition()) {
|
| - reportError(position(), ParserErrorCode.VAR_IS_NOT_ALLOWED_ON_A_METHOD_DEFINITION);
|
| - member = parseMethodOrAccessor(modifiers, null);
|
| - break;
|
| - }
|
| -
|
| - member = parseFieldDeclaration(modifiers, null);
|
| - expectStatmentTerminator();
|
| - break;
|
| - }
|
| -
|
| - case CONST: {
|
| - consume(Token.CONST);
|
| - modifiers = modifiers.makeConstant();
|
| - // Allow "const factory ... native" constructors for core libraries only
|
| - if (optionalPseudoKeyword(FACTORY_KEYWORD)) {
|
| - modifiers = modifiers.makeFactory();
|
| - }
|
| - if (peek(0).equals(Token.IDENTIFIER) && looksLikeMethodOrAccessorDefinition()) {
|
| - return done(parseMethod(modifiers, null));
|
| - }
|
| - // Try to find type, may be "const ^ Type field".
|
| - DartTypeNode type = null;
|
| - if (peek(1) != Token.COMMA
|
| - && peek(1) != Token.ASSIGN
|
| - && peek(1) != Token.SEMICOLON) {
|
| - type = parseTypeAnnotation();
|
| - }
|
| - // Parse field.
|
| - modifiers = modifiers.makeFinal();
|
| - member = parseFieldDeclaration(modifiers, type);
|
| - expectStatmentTerminator();
|
| - break;
|
| - }
|
| -
|
| - case FINAL: {
|
| - consume(Token.FINAL);
|
| - modifiers = modifiers.makeFinal();
|
| -
|
| - // Check for malformed method starting with 'final': final ^ foo() { }
|
| - if (peek(0).equals(Token.IDENTIFIER) && looksLikeMethodOrAccessorDefinition()) {
|
| - reportError(position(), ParserErrorCode.FINAL_IS_NOT_ALLOWED_ON_A_METHOD_DEFINITION);
|
| - member = parseMethodOrAccessor(modifiers, null);
|
| - break;
|
| - }
|
| - DartTypeNode type = null;
|
| - if (peek(1) != Token.COMMA
|
| - && peek(1) != Token.ASSIGN
|
| - && peek(1) != Token.SEMICOLON) {
|
| - type = parseTypeAnnotation();
|
| -
|
| - // Check again for malformed method starting with 'final': final String ^ foo() { }
|
| - if (peek(0).equals(Token.IDENTIFIER) && looksLikeMethodOrAccessorDefinition()) {
|
| - reportError(position(), ParserErrorCode.FINAL_IS_NOT_ALLOWED_ON_A_METHOD_DEFINITION);
|
| - member = parseMethodOrAccessor(modifiers, null);
|
| - break;
|
| - }
|
| - }
|
| - member = parseFieldDeclaration(modifiers, type);
|
| - expectStatmentTerminator();
|
| - break;
|
| - }
|
| -
|
| - case IDENTIFIER: {
|
| -
|
| - // Check to see if it looks like the start of a method definition (sans type).
|
| - if (looksLikeMethodOrAccessorDefinition()) {
|
| - member = parseMethodOrAccessor(modifiers, null);
|
| - break;
|
| - }
|
| - }
|
| - //$FALL-THROUGH$
|
| -
|
| - case VOID: {
|
| -
|
| - // The next token may be a type specification or parameterized constructor: either a method or field.
|
| - boolean isVoidType = peek(0) == Token.VOID;
|
| - DartTypeNode type;
|
| - if (isVoidType) {
|
| - type = parseVoidType();
|
| - } else {
|
| - int nameIndex = skipTypeName(0);
|
| - if (nameIndex < 0 || peek(nameIndex) != Token.IDENTIFIER) {
|
| - // There was no type name.
|
| - type = null;
|
| - } else {
|
| - type = parseTypeAnnotation();
|
| - }
|
| - }
|
| - if (peek(1) == Token.SEMICOLON
|
| - || peek(1) == Token.COMMA
|
| - || peek(1) == Token.ASSIGN) {
|
| - if (modifiers.isAbstract()) {
|
| - reportError(position(), ParserErrorCode.INVALID_FIELD_DECLARATION);
|
| - }
|
| - member = parseFieldDeclaration(modifiers, type);
|
| - if (isVoidType) {
|
| - reportError(type, ParserErrorCode.VOID_FIELD);
|
| - } else if (!modifiers.isFinal() && type == null) {
|
| - reportError(position(), ParserErrorCode.INVALID_FIELD_DECLARATION);
|
| - }
|
| - expectStatmentTerminator();
|
| - } else {
|
| - member = parseMethodOrAccessor(modifiers, type);
|
| - }
|
| - break;
|
| - }
|
| -
|
| - case SEMICOLON:
|
| - default: {
|
| - done(null);
|
| - reportUnexpectedToken(position(), null, next());
|
| - member = null;
|
| - break;
|
| - }
|
| - }
|
| - return member;
|
| - }
|
| -
|
| - /**
|
| - * Returns true if the beginning of a method definition follows.
|
| - *
|
| - * This test is needed to disambiguate between a method that returns a type
|
| - * and a plain method.
|
| - *
|
| - * Assumes the next token has already been determined to be an identifier.
|
| - *
|
| - * The following constructs will match:
|
| - *
|
| - * : get (
|
| - * | get identifier (
|
| - * | set (
|
| - * | set identifier (
|
| - * | operator (
|
| - * | operator <op> (
|
| - * | identifier (
|
| - * | identifier DOT identifier (
|
| - * | identifier DOT identifier DOT identifier (
|
| - *
|
| - * @return <code>true</code> if the signature of a method has been found. No tokens are consumed.
|
| - */
|
| - private boolean looksLikeMethodOrAccessorDefinition() {
|
| - assert (peek(0) == Token.IDENTIFIER );
|
| - beginMethodName(); // begin() equivalent
|
| - try {
|
| - if (isBuiltInSpecial() && peekPseudoKeyword(0, OPERATOR_KEYWORD)) {
|
| - next();
|
| - // Using 'operator' as a field name is valid
|
| - if (peek(0).equals(Token.SEMICOLON) || peek(0).equals(Token.ASSIGN)) {
|
| - return false;
|
| - }
|
| - // Using 'operator' as a method name is valid (but discouraged)
|
| - if (peek(0).equals(Token.LPAREN)) {
|
| - return true;
|
| - }
|
| - // operator call (
|
| - if (peekPseudoKeyword(0, CALL_KEYWORD) && peek(1).equals(Token.LPAREN)) {
|
| - return true;
|
| - }
|
| - // TODO(zundel): Look for valid operator overload tokens. For now just assuming
|
| - // non-idents are good enough
|
| - // operator ??? (
|
| - if (!(peek(0).equals(Token.IDENTIFIER) && peek(1).equals(Token.LPAREN))) {
|
| - return true;
|
| - }
|
| - if (peek(0).equals(Token.LBRACK) && peek(1).equals(Token.RBRACK)) {
|
| - // operator [] (
|
| - if (peek(2).equals(Token.LPAREN)) {
|
| - return true;
|
| - }
|
| - // operator []= (
|
| - if (peek(2).equals(Token.ASSIGN) && peek(3).equals(Token.LPAREN)) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - if (peekPseudoKeyword(0, GETTER_KEYWORD)
|
| - || peekPseudoKeyword(0, SETTER_KEYWORD)) {
|
| - boolean isGetter = peekPseudoKeyword(0, GETTER_KEYWORD);
|
| - next();
|
| - // Using 'get' or 'set' as a field name is valid
|
| - if (peek(0).equals(Token.SEMICOLON) || peek(0).equals(Token.ASSIGN)) {
|
| - return false;
|
| - }
|
| - // Using 'get' or 'set' as a method name is valid (but discouraged)
|
| - if (peek(0).equals(Token.LPAREN)) {
|
| - return true;
|
| - }
|
| - // normal case: get foo (
|
| - if (peek(0).equals(Token.IDENTIFIER) && (isGetter || peek(1).equals(Token.LPAREN))) {
|
| - return true;
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - consume(Token.IDENTIFIER);
|
| -
|
| - if (peek(0).equals(Token.PERIOD) && peek(1).equals(Token.IDENTIFIER)) {
|
| - consume(Token.PERIOD);
|
| - consume(Token.IDENTIFIER);
|
| -
|
| - if (peek(0).equals(Token.PERIOD) && peek(1).equals(Token.IDENTIFIER)) {
|
| - consume(Token.PERIOD);
|
| - consume(Token.IDENTIFIER);
|
| - }
|
| - }
|
| -
|
| - // next token should be LPAREN
|
| - return (peek(0).equals(Token.LPAREN));
|
| - } finally {
|
| - rollback();
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * factoryConstructorDeclaration
|
| - * : FACTORY qualified ('.' identifier)? formalParameterList
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private DartMethodDefinition parseFactory(Modifiers modifiers) {
|
| - beginMethodName();
|
| - DartExpression name = parseQualified(true);
|
| - if (optional(Token.PERIOD)) {
|
| - name = doneWithoutConsuming(new DartPropertyAccess(name, parseIdentifier()));
|
| - }
|
| - done(name);
|
| - FormalParameters formals = parseFormalParameterList();
|
| - int parametersCloseParen = ctx.getTokenLocation().getBegin();
|
| -
|
| - // Parse redirecting factory
|
| - if (match(Token.ASSIGN)) {
|
| - next();
|
| - if (!modifiers.isFactory()) {
|
| - reportError(position(), ParserErrorCode.ONLY_FACTORIES_CAN_REDIRECT);
|
| - }
|
| - modifiers = modifiers.makeRedirectedConstructor();
|
| - DartTypeNode redirectedTypeName = parseTypeAnnotationPossiblyFollowedByName();
|
| - DartIdentifier redirectedConstructorName = null;
|
| - if (optional(Token.PERIOD)) {
|
| - redirectedConstructorName = parseIdentifier();
|
| - }
|
| - expect(Token.SEMICOLON);
|
| - DartFunction function = doneWithoutConsuming(new DartFunction(formals.val, formals.openParen,
|
| - formals.optionalOpenOffset, formals.optionalCloseOffset, parametersCloseParen, null, null));
|
| - return DartMethodDefinition.create(name, function, modifiers, redirectedTypeName,
|
| - redirectedConstructorName);
|
| - }
|
| -
|
| - DartFunction function;
|
| - if (peekPseudoKeyword(0, NATIVE_KEYWORD)) {
|
| - modifiers = modifiers.makeNative();
|
| - function = new DartFunction(formals.val, formals.optionalOpenOffset,
|
| - formals.optionalOpenOffset, formals.optionalCloseOffset, parametersCloseParen,
|
| - parseNativeBlock(modifiers), null);
|
| - } else {
|
| - function = new DartFunction(formals.val, formals.optionalOpenOffset,
|
| - formals.optionalOpenOffset, formals.optionalCloseOffset, parametersCloseParen,
|
| - parseFunctionStatementBody(!modifiers.isExternal(), true), null);
|
| - }
|
| - doneWithoutConsuming(function);
|
| - return DartMethodDefinition.create(name, function, modifiers, null);
|
| - }
|
| -
|
| - private DartIdentifier parseVoidIdentifier() {
|
| - beginIdentifier();
|
| - expect(Token.VOID);
|
| - return done(new DartIdentifier(Token.VOID.getSyntax()));
|
| - }
|
| -
|
| - private DartTypeNode parseVoidType() {
|
| - beginTypeAnnotation();
|
| - return done(new DartTypeNode(parseVoidIdentifier()));
|
| - }
|
| -
|
| - private DartMethodDefinition parseMethod(Modifiers modifiers, DartTypeNode returnType) {
|
| - DartExpression name = new DartIdentifier("");
|
| -
|
| - if (modifiers.isFactory()) {
|
| - if (modifiers.isAbstract()) {
|
| - reportError(position(), ParserErrorCode.FACTORY_CANNOT_BE_ABSTRACT);
|
| - }
|
| - if (modifiers.isStatic()) {
|
| - reportError(position(), ParserErrorCode.FACTORY_CANNOT_BE_STATIC);
|
| - }
|
| - }
|
| -
|
| - int arity = -1;
|
| - Token operation = null;
|
| - if (isBuiltInSpecial() && optionalPseudoKeyword(OPERATOR_KEYWORD)) {
|
| - // Overloaded operator.
|
| - if (modifiers.isStatic()) {
|
| - reportError(position(), ParserErrorCode.OPERATOR_CANNOT_BE_STATIC);
|
| - }
|
| - modifiers = modifiers.makeOperator();
|
| -
|
| - beginOperatorName();
|
| - operation = next();
|
| - if (operation.isUserDefinableOperator()) {
|
| - name = done(new DartIdentifier(operation.getSyntax()));
|
| - if (operation == Token.ASSIGN_INDEX) {
|
| - arity = 2;
|
| - } else if (operation == Token.SUB) {
|
| - arity = -1;
|
| - } else if (operation.isBinaryOperator()) {
|
| - arity = 1;
|
| - } else if (operation == Token.INDEX) {
|
| - arity = 1;
|
| - } else {
|
| - assert operation.isUnaryOperator();
|
| - arity = 0;
|
| - }
|
| - } else if (operation == Token.IDENTIFIER
|
| - && ctx.getTokenString().equals(CALL_KEYWORD)) {
|
| - name = done(new DartIdentifier(CALL_KEYWORD));
|
| - arity = -1;
|
| - } else if (operation == Token.IDENTIFIER
|
| - && ctx.getTokenString().equals(CALL_KEYWORD)) {
|
| - name = done(new DartIdentifier(CALL_KEYWORD));
|
| - } else {
|
| - // Not a valid operator. Try to recover.
|
| - boolean found = false;
|
| - for (int i = 0; i < 4; ++i) {
|
| - if (peek(i).equals(Token.LPAREN)) {
|
| - found = true;
|
| - break;
|
| - }
|
| - }
|
| - StringBuilder buf = new StringBuilder();
|
| - buf.append(operation.getSyntax());
|
| - if (found) {
|
| - reportError(position(), ParserErrorCode.OPERATOR_IS_NOT_USER_DEFINABLE);
|
| - while(true) {
|
| - Token token = peek(0);
|
| - if (token.equals(Token.LPAREN)) {
|
| - break;
|
| - }
|
| - buf.append(next().getSyntax());
|
| - }
|
| - name = done(new DartIdentifier(buf.toString()));
|
| - } else {
|
| - reportUnexpectedToken(position(), Token.COMMENT, operation);
|
| - done(null);
|
| - }
|
| - }
|
| - } else {
|
| - beginMethodName();
|
| - // Check for getters and setters.
|
| - if (peek(1) != Token.LPAREN && optionalPseudoKeyword(GETTER_KEYWORD)) {
|
| - name = parseIdentifier();
|
| - modifiers = modifiers.makeGetter();
|
| - arity = 0;
|
| - } else if (peek(1) != Token.LPAREN && optionalPseudoKeyword(SETTER_KEYWORD)) {
|
| - name = parseIdentifier();
|
| - modifiers = modifiers.makeSetter();
|
| - arity = 1;
|
| - } else {
|
| - // Normal method or property.
|
| - name = parseIdentifier();
|
| - }
|
| -
|
| - // Check for named constructor.
|
| - if (optional(Token.PERIOD)) {
|
| - name = doneWithoutConsuming(new DartPropertyAccess(name, parseIdentifier()));
|
| - if(currentlyParsingToplevel()) {
|
| - // TODO: Error recovery could find a missing brace and treat this as an expression
|
| - reportError(name, ParserErrorCode.FUNCTION_NAME_EXPECTED_IDENTIFIER);
|
| - }
|
| - if (optional(Token.PERIOD)) {
|
| - name = doneWithoutConsuming(new DartPropertyAccess(name, parseIdentifier()));
|
| - }
|
| - }
|
| - done(null);
|
| - }
|
| -
|
| - // Parse the parameters definitions.
|
| - FormalParameters parametersInfo;
|
| - if (modifiers.isGetter()) {
|
| - parametersInfo = new FormalParameters(new ArrayList<DartParameter>(), -1, -1, -1);
|
| - if (peek(0) == Token.LPAREN) {
|
| - reportError(position(), ParserErrorCode.DEPRECATED_GETTER);
|
| - parametersInfo = parseFormalParameterList();
|
| - }
|
| - } else {
|
| - parametersInfo = parseFormalParameterList();
|
| - }
|
| - List<DartParameter> parameters = parametersInfo.val;
|
| - int parametersCloseParen = ctx.getTokenLocation().getBegin();
|
| -
|
| - if (arity != -1) {
|
| - if (parameters.size() != arity) {
|
| - reportError(position(), ParserErrorCode.ILLEGAL_NUMBER_OF_PARAMETERS);
|
| - }
|
| - // In methods with required arity each parameter is required.
|
| - for (int i = 0, size = parameters.size(); i < size; i++) {
|
| - DartParameter parameter = parameters.get(i);
|
| - if (parameter.getModifiers().isOptional()) {
|
| - reportError(parameter, ParserErrorCode.OPTIONAL_POSITIONAL_PARAMETER_NOT_ALLOWED);
|
| - }
|
| - if (parameter.getModifiers().isNamed()) {
|
| - reportError(parameter, ParserErrorCode.NAMED_PARAMETER_NOT_ALLOWED);
|
| - }
|
| - }
|
| - } else if (operation == Token.SUB) {
|
| - if (parameters.size() != 0 && parameters.size() != 1) {
|
| - reportError(position(), ParserErrorCode.ILLEGAL_NUMBER_OF_PARAMETERS);
|
| - }
|
| - // In methods with required arity each parameter is required.
|
| - for (int i = 0, size = parameters.size(); i < size; i++) {
|
| - DartParameter parameter = parameters.get(i);
|
| - if (parameter.getModifiers().isNamed()) {
|
| - reportError(parameter, ParserErrorCode.NAMED_PARAMETER_NOT_ALLOWED);
|
| - }
|
| - }
|
| - }
|
| -
|
| - // Parse redirecting factory
|
| - DartTypeNode redirectedTypeName = null;
|
| - DartIdentifier redirectedConstructorName = null;
|
| - if (match(Token.ASSIGN)) {
|
| - next();
|
| - if (!modifiers.isFactory()) {
|
| - reportError(position(), ParserErrorCode.ONLY_FACTORIES_CAN_REDIRECT);
|
| - }
|
| - modifiers = modifiers.makeRedirectedConstructor();
|
| - redirectedTypeName = parseTypeAnnotationPossiblyFollowedByName();
|
| - if (optional(Token.PERIOD)) {
|
| - redirectedConstructorName = parseIdentifier();
|
| - }
|
| - expect(Token.SEMICOLON);
|
| - DartFunction function = doneWithoutConsuming(new DartFunction(parameters,
|
| - parametersInfo.openParen, parametersInfo.optionalOpenOffset,
|
| - parametersInfo.optionalCloseOffset, parametersCloseParen, null, returnType));
|
| - return DartMethodDefinition.create(name, function, modifiers, redirectedTypeName,
|
| - redirectedConstructorName);
|
| - }
|
| -
|
| - // Parse initializer expressions for constructors.
|
| - List<DartInitializer> initializers = new ArrayList<DartInitializer>();
|
| - if (match(Token.COLON) && !(isParsingInterface || modifiers.isFactory())) {
|
| - parseInitializers(initializers);
|
| - boolean isRedirectedConstructor = validateInitializers(parameters, initializers);
|
| - if (isRedirectedConstructor) {
|
| - modifiers = modifiers.makeRedirectedConstructor();
|
| - }
|
| - }
|
| -
|
| - // Parse the body.
|
| - DartBlock body = null;
|
| - if (!optional(Token.SEMICOLON)) {
|
| - if (peekPseudoKeyword(0, NATIVE_KEYWORD)) {
|
| - modifiers = modifiers.makeNative();
|
| - body = parseNativeBlock(modifiers);
|
| - } else {
|
| - body = parseFunctionStatementBody(!modifiers.isExternal(), true);
|
| - }
|
| - if (body != null && modifiers.isRedirectedConstructor()) {
|
| - reportError(position(), ParserErrorCode.REDIRECTING_CONSTRUCTOR_CANNOT_HAVE_A_BODY);
|
| - }
|
| - }
|
| -
|
| - DartFunction function = doneWithoutConsuming(new DartFunction(parameters,
|
| - parametersInfo.openParen, parametersInfo.optionalOpenOffset,
|
| - parametersInfo.optionalCloseOffset, parametersCloseParen, body, returnType));
|
| - return DartMethodDefinition.create(name, function, modifiers, initializers);
|
| - }
|
| -
|
| - private DartBlock parseNativeBlock(Modifiers modifiers) {
|
| - beginNativeBody();
|
| - if (!optionalPseudoKeyword(NATIVE_KEYWORD)) {
|
| - throw new AssertionError();
|
| - }
|
| - if (!allowNativeKeyword) {
|
| - reportError(position(), ParserErrorCode.NATIVE_ONLY_CORE_LIB);
|
| - }
|
| - DartExpression body = null;
|
| - if (match(Token.STRING)) {
|
| - body = parseStringWithPasting();
|
| - }
|
| - if (match(Token.LBRACE) || match(Token.ARROW)) {
|
| - return done(parseFunctionStatementBody(!modifiers.isExternal(), true));
|
| - } else {
|
| - expect(Token.SEMICOLON);
|
| - return done(new DartNativeBlock(body));
|
| - }
|
| - }
|
| -
|
| - private DartNodeWithMetadata parseMethodOrAccessor(Modifiers modifiers, DartTypeNode returnType) {
|
| - DartMethodDefinition method = done(parseMethod(modifiers, returnType));
|
| - // Abstract method can not have a body.
|
| - if (method.getFunction().getBody() != null) {
|
| - if (isParsingInterface) {
|
| - reportError(method.getName(), ParserErrorCode.INTERFACE_METHOD_WITH_BODY);
|
| - }
|
| - if (method.getModifiers().isAbstract()) {
|
| - reportError(method.getName(), ParserErrorCode.ABSTRACT_METHOD_WITH_BODY);
|
| - }
|
| - }
|
| - // If getter or setter, generate DartFieldDefinition instead.
|
| - if (method.getModifiers().isGetter() || method.getModifiers().isSetter()) {
|
| - DartField field = new DartField((DartIdentifier) method.getName(),
|
| - method.getModifiers().makeAbstractField(), method, null);
|
| - field.setSourceInfo(method.getSourceInfo());
|
| - DartFieldDefinition fieldDefinition =
|
| - new DartFieldDefinition(null, Lists.<DartField>create(field));
|
| - fieldDefinition.setSourceInfo(field.getSourceInfo());
|
| - return fieldDefinition;
|
| - }
|
| - // OK, use method as method.
|
| - return method;
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * initializers
|
| - * : ':' superCallOrFirstFieldInitializer (',' fieldInitializer)*
|
| - * | THIS ('.' identifier) formalParameterList
|
| - * ;
|
| - *
|
| - * fieldInitializer
|
| - * : (THIS '.')? identifier '=' conditionalExpression
|
| - * ;
|
| - *
|
| - * superCallOrFirstFieldInitializer
|
| - * : SUPER arguments | SUPER '.' identifier arguments
|
| - * | fieldInitializer
|
| - * ;
|
| - *
|
| - * fieldInitializer
|
| - * : (THIS '.')? identifier '=' conditionalExpression
|
| - * | THIS ('.' identifier)? arguments
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private void parseInitializers(List<DartInitializer> initializers) {
|
| - expect(Token.COLON);
|
| - do {
|
| - beginInitializer();
|
| - if (match(Token.SUPER)) {
|
| - beginSuperInitializer();
|
| - expect(Token.SUPER);
|
| - DartIdentifier constructor = null;
|
| - if (optional(Token.PERIOD)) {
|
| - constructor = parseIdentifier();
|
| - }
|
| - DartSuperConstructorInvocation superInvocation =
|
| - new DartSuperConstructorInvocation(constructor, parseArguments());
|
| - initializers.add(done(new DartInitializer(null, done(superInvocation))));
|
| - } else {
|
| - boolean hasThisPrefix = optional(Token.THIS);
|
| - if (hasThisPrefix) {
|
| - if (match(Token.LPAREN)) {
|
| - parseRedirectedConstructorInvocation(null, initializers);
|
| - continue;
|
| - }
|
| - expect(Token.PERIOD);
|
| - }
|
| - DartIdentifier name = parseIdentifier();
|
| - if (hasThisPrefix && match(Token.LPAREN)) {
|
| - parseRedirectedConstructorInvocation(name, initializers);
|
| - continue;
|
| - } else {
|
| - expect(Token.ASSIGN);
|
| - boolean save = setAllowFunctionExpression(false);
|
| - DartExpression initExpr = parseExpression();
|
| - setAllowFunctionExpression(save);
|
| - initializers.add(done(new DartInitializer(name, initExpr)));
|
| - }
|
| - }
|
| - } while (optional(Token.COMMA));
|
| - }
|
| -
|
| - private void parseRedirectedConstructorInvocation(DartIdentifier name,
|
| - List<DartInitializer> initializers) {
|
| - DartRedirectConstructorInvocation redirConstructor =
|
| - new DartRedirectConstructorInvocation(name, parseArguments());
|
| - initializers.add(done(new DartInitializer(null, doneWithoutConsuming(redirConstructor))));
|
| - }
|
| -
|
| - private boolean validateInitializers(List<DartParameter> parameters,
|
| - List<DartInitializer> initializers) {
|
| - // Try to find DartRedirectConstructorInvocation, check for multiple invocations.
|
| - // Check for DartSuperConstructorInvocation multiple invocations.
|
| - DartInitializer redirectInitializer = null;
|
| - boolean firstMultipleRedirectReported = false;
|
| - {
|
| - DartInitializer superInitializer = null;
|
| - boolean firstMultipleSuperReported = false;
|
| - for (DartInitializer initializer : initializers) {
|
| - if (initializer.isInvocation()) {
|
| - // DartSuperConstructorInvocation
|
| - DartExpression initializerInvocation = initializer.getValue();
|
| - if (initializerInvocation instanceof DartSuperConstructorInvocation) {
|
| - if (superInitializer != null) {
|
| - if (!firstMultipleSuperReported) {
|
| - reportError(superInitializer, ParserErrorCode.SUPER_CONSTRUCTOR_MULTIPLE);
|
| - firstMultipleSuperReported = true;
|
| - }
|
| - reportError(initializer, ParserErrorCode.SUPER_CONSTRUCTOR_MULTIPLE);
|
| - } else {
|
| - superInitializer = initializer;
|
| - }
|
| - }
|
| - // DartRedirectConstructorInvocation
|
| - if (initializerInvocation instanceof DartRedirectConstructorInvocation) {
|
| - if (redirectInitializer != null) {
|
| - if (!firstMultipleRedirectReported) {
|
| - reportError(redirectInitializer, ParserErrorCode.REDIRECTING_CONSTRUCTOR_MULTIPLE);
|
| - firstMultipleRedirectReported = true;
|
| - }
|
| - reportError(initializer, ParserErrorCode.REDIRECTING_CONSTRUCTOR_MULTIPLE);
|
| - } else {
|
| - redirectInitializer = initializer;
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| - // If there is redirecting constructor, then there should be no other initializers.
|
| - if (redirectInitializer != null) {
|
| - boolean shouldRedirectInvocationReported = false;
|
| - // Implicit initializer in form of "this.id" parameter.
|
| - for (DartParameter parameter : parameters) {
|
| - if (parameter.getName() instanceof DartPropertyAccess) {
|
| - DartPropertyAccess propertyAccess = (DartPropertyAccess) parameter.getName();
|
| - if (propertyAccess.getQualifier() instanceof DartThisExpression) {
|
| - shouldRedirectInvocationReported = true;
|
| - reportError(
|
| - parameter,
|
| - ParserErrorCode.REDIRECTING_CONSTRUCTOR_PARAM);
|
| - }
|
| - }
|
| - }
|
| - // Iterate all initializers and mark all except of DartRedirectConstructorInvocation
|
| - for (DartInitializer initializer : initializers) {
|
| - if (!(initializer.getValue() instanceof DartRedirectConstructorInvocation)) {
|
| - shouldRedirectInvocationReported = true;
|
| - reportError(
|
| - initializer,
|
| - ParserErrorCode.REDIRECTING_CONSTRUCTOR_OTHER);
|
| - }
|
| - }
|
| - // Mark DartRedirectConstructorInvocation if needed.
|
| - if (shouldRedirectInvocationReported) {
|
| - reportError(
|
| - redirectInitializer,
|
| - ParserErrorCode.REDIRECTING_CONSTRUCTOR_ITSELF);
|
| - }
|
| - }
|
| - // Done.
|
| - return redirectInitializer != null;
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * variableDeclaration
|
| - * : constVarOrType identifierList
|
| - * ;
|
| - * identifierList
|
| - * : identifier (',' identifier)*
|
| - * ;
|
| - *
|
| - * staticConstDeclarationList
|
| - * : staticConstDeclaration (',' staticConstDeclaration)*
|
| - * ;
|
| - *
|
| - * staticConstDeclaration
|
| - * : identifier '=' constantExpression
|
| - * ;
|
| - *
|
| - * // The compile-time expression production is used to mark certain expressions
|
| - * // as only being allowed to hold a compile-time constant. The grammar cannot
|
| - * // express these restrictions, so this will have to be enforced by a separate
|
| - * // analysis phase.
|
| - * constantExpression
|
| - * : expression
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private DartFieldDefinition parseFieldDeclaration(Modifiers modifiers, DartTypeNode type) {
|
| - List<DartField> fields = new ArrayList<DartField>();
|
| - List<DartAnnotation> metadata = parseMetadata();
|
| - do {
|
| - beginVariableDeclaration();
|
| - DartIdentifier name = parseIdentifier();
|
| - DartExpression value = null;
|
| - if (optional(Token.ASSIGN)) {
|
| - value = parseExpression();
|
| - if (value != null) {
|
| - modifiers = modifiers.makeInitialized();
|
| - }
|
| - }
|
| - if (modifiers.isExternal()) {
|
| - reportError(name, ParserErrorCode.EXTERNAL_ONLY_METHOD);
|
| - }
|
| - DartField field = done(new DartField(name, modifiers, null, value));
|
| - setMetadata(field, metadata);
|
| - fields.add(field);
|
| - } while (optional(Token.COMMA));
|
| - DartFieldDefinition definition = new DartFieldDefinition(type, fields);
|
| - setMetadata(definition, metadata);
|
| - return done(definition);
|
| - }
|
| -
|
| - private static class FormalParameters {
|
| - private final List<DartParameter> val;
|
| - private final int openParen;
|
| - private final int optionalOpenOffset;
|
| - private final int optionalCloseOffset;
|
| - public FormalParameters(List<DartParameter> parameters, int openParen, int optionalOpenOffset,
|
| - int optionalCloseOffset) {
|
| - this.val = parameters;
|
| - this.openParen = openParen;
|
| - this.optionalOpenOffset = optionalOpenOffset;
|
| - this.optionalCloseOffset = optionalCloseOffset;
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * formalParameterList
|
| - * : '(' ')'
|
| - * | '(' normalFormalParameters (',' optionalFormalParameters)? ')'
|
| - * | '(' optionalFormalParameters ')'
|
| - * ;
|
| - *
|
| - * normalFormalParameters
|
| - * : normalFormalParameter (',' normalFormalParameter)*
|
| - * ;
|
| - *
|
| - * optionalFormalParameters
|
| - * : optionalPositionalFormalParameters
|
| - * | namedFormalParameters
|
| - * ;
|
| - *
|
| - * optionalPositionalFormalParameters
|
| - * : '[' defaultFormalParameter (',' defaultFormalParameter)* ']'
|
| - * ;
|
| - *
|
| - * namedFormalParameters
|
| - * : '{' defaultNamedParameter (',' defaultNamedParameter)* '}'
|
| - * ;
|
| - * </pre>
|
| - */
|
| - @Terminals(tokens = {Token.COMMA, Token.RPAREN})
|
| - private FormalParameters parseFormalParameterList() {
|
| - beginFormalParameterList();
|
| - List<DartParameter> params = new ArrayList<DartParameter>();
|
| - int optionalOpenOffset = -1;
|
| - int optionalCloseOffset = -1;
|
| - expect(Token.LPAREN);
|
| - boolean done = optional(Token.RPAREN);
|
| - int openParen = ctx.getTokenLocation().getBegin();
|
| - boolean isOptional = false;
|
| - boolean isNamed = false;
|
| - while (!done) {
|
| - if (!isOptional && optional(Token.LBRACK)) {
|
| - if (isNamed) {
|
| - reportErrorWithoutAdvancing(ParserErrorCode.CANNOT_MIX_OPTIONAL_AND_NAMED_PARAMETERS);
|
| - }
|
| - isOptional = true;
|
| - optionalOpenOffset = ctx.getTokenLocation().getBegin();
|
| - }
|
| - if (!isNamed && optional(Token.LBRACE)) {
|
| - if (isOptional) {
|
| - reportErrorWithoutAdvancing(ParserErrorCode.CANNOT_MIX_OPTIONAL_AND_NAMED_PARAMETERS);
|
| - }
|
| - isNamed = true;
|
| - optionalOpenOffset = ctx.getTokenLocation().getBegin();
|
| - }
|
| -
|
| - DartParameter param = parseFormalParameter(isOptional, isNamed);
|
| - params.add(param);
|
| -
|
| - if (isOptional && optional(Token.RBRACK)) {
|
| - optionalCloseOffset = ctx.getTokenLocation().getBegin();
|
| - expectCloseParen();
|
| - break;
|
| - }
|
| - if (isNamed && optional(Token.RBRACE)) {
|
| - optionalCloseOffset = ctx.getTokenLocation().getBegin();
|
| - expectCloseParen();
|
| - break;
|
| - }
|
| -
|
| - // Ensure termination if token is anything other than COMMA.
|
| - // Must keep Token.COMMA in sync with @Terminals above
|
| - if (!optional(Token.COMMA)) {
|
| - if (isOptional && !optional(Token.RBRACE)) {
|
| - reportErrorWithoutAdvancing(ParserErrorCode.MISSING_OPTIONAL_PARAMETER_END);
|
| - }
|
| - if (isNamed && !optional(Token.RBRACK)) {
|
| - reportErrorWithoutAdvancing(ParserErrorCode.MISSING_NAMED_PARAMETER_END);
|
| - }
|
| - // Must keep Token.RPAREN in sync with @Terminals above
|
| - expectCloseParen();
|
| - done = true;
|
| - }
|
| - }
|
| -
|
| - return new FormalParameters(done(params), openParen, optionalOpenOffset, optionalCloseOffset);
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * normalFormalParameter
|
| - * : functionDeclaration
|
| - * | fieldFormalParameter
|
| - * | simpleFormalParameter
|
| - * ;
|
| - *
|
| - * defaultFormalParameter
|
| - * : normalFormalParameter ('=' constantExpression)?
|
| - * ;
|
| - *
|
| - * defaultNamedParameter
|
| - * : normalFormalParameter (':' constantExpression)?
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private DartParameter parseFormalParameter(boolean isOptional, boolean isNamed) {
|
| - beginFormalParameter();
|
| - List<DartAnnotation> metadata = parseMetadata();
|
| - DartExpression paramName = null;
|
| - DartTypeNode type = null;
|
| - DartExpression defaultExpr = null;
|
| - List<DartParameter> functionParams = null;
|
| - boolean hasVar = false;
|
| - Modifiers modifiers = Modifiers.NONE;
|
| -
|
| - if (isOptional) {
|
| - modifiers = modifiers.makeOptional();
|
| - }
|
| - if (isNamed) {
|
| - modifiers = modifiers.makeNamed();
|
| - }
|
| -
|
| - if (optional(Token.FINAL)) {
|
| - modifiers = modifiers.makeFinal();
|
| - } else if (optional(Token.CONST)) {
|
| - reportError(position(), ParserErrorCode.FORMAL_PARAMETER_IS_CONST);
|
| - } else if (optional(Token.VAR)) {
|
| - hasVar = true;
|
| - }
|
| -
|
| - boolean isVoidType = false;
|
| - if (!hasVar) {
|
| - isVoidType = (peek(0) == Token.VOID);
|
| - if (isVoidType) {
|
| - type = parseVoidType();
|
| - } else if ((peek(0) != Token.ELLIPSIS)
|
| - && (peek(1) != Token.COMMA)
|
| - && (peek(1) != Token.RPAREN)
|
| - && (peek(1) != Token.RBRACE)
|
| - && (peek(1) != Token.RBRACK)
|
| - && (peek(1) != Token.ASSIGN)
|
| - && (peek(1) != Token.COLON)
|
| - && (peek(1) != Token.LPAREN)
|
| - && (peek(0) != Token.THIS)) {
|
| - // Must be a type specification.
|
| - type = parseTypeAnnotation();
|
| - }
|
| - }
|
| -
|
| - paramName = parseParameterName();
|
| -
|
| - if (peek(0) == Token.LPAREN) {
|
| - // Function parameter.
|
| - if (modifiers.isFinal()) {
|
| - reportError(position(), ParserErrorCode.FUNCTION_TYPED_PARAMETER_IS_FINAL);
|
| - }
|
| - if (hasVar) {
|
| - reportError(position(), ParserErrorCode.FUNCTION_TYPED_PARAMETER_IS_VAR);
|
| - }
|
| - functionParams = parseFormalParameterList().val;
|
| - validateNoDefaultParameterValues(
|
| - functionParams,
|
| - ParserErrorCode.DEFAULT_VALUE_CAN_NOT_BE_SPECIFIED_IN_CLOSURE);
|
| - } else {
|
| - // Not a function parameter.
|
| - if (isVoidType) {
|
| - reportError(type, ParserErrorCode.VOID_PARAMETER);
|
| - }
|
| - }
|
| -
|
| - // Look for an initialization expression
|
| - switch (peek(0)) {
|
| - case COMMA:
|
| - case RPAREN:
|
| - case RBRACE:
|
| - case RBRACK:
|
| - // It is a simple parameter.
|
| - break;
|
| -
|
| - case ASSIGN:
|
| - // Default parameter -- only allowed for optional parameters.
|
| - if (isOptional) {
|
| - consume(Token.ASSIGN);
|
| - defaultExpr = parseExpression();
|
| - } else if (isNamed) {
|
| - reportError(position(), ParserErrorCode.INVALID_SEPARATOR_FOR_NAMED);
|
| - consume(Token.ASSIGN);
|
| - defaultExpr = parseExpression();
|
| - } else {
|
| - reportError(position(), ParserErrorCode.DEFAULT_POSITIONAL_PARAMETER);
|
| - }
|
| - break;
|
| -
|
| - case COLON:
|
| - // Default parameter -- only allowed for named parameters.
|
| - if (isNamed) {
|
| - consume(Token.COLON);
|
| - defaultExpr = parseExpression();
|
| - } else if (isOptional) {
|
| - reportError(position(), ParserErrorCode.INVALID_SEPARATOR_FOR_OPTIONAL);
|
| - consume(Token.COLON);
|
| - defaultExpr = parseExpression();
|
| - } else {
|
| - reportError(position(), ParserErrorCode.DEFAULT_POSITIONAL_PARAMETER);
|
| - }
|
| - break;
|
| -
|
| - default:
|
| - reportUnexpectedToken(position(), null, peek(0));
|
| - break;
|
| - }
|
| -
|
| - DartParameter parameter = new DartParameter(paramName, type, functionParams, defaultExpr,
|
| - modifiers);
|
| - setMetadata(parameter, metadata);
|
| - return done(parameter);
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * simpleFormalParameter
|
| - * : declaredIdentifier
|
| - * | identifier
|
| - * ;
|
| - *
|
| - * fieldFormalParameter
|
| - * : finalVarOrType? THIS '.' identifier
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private DartExpression parseParameterName() {
|
| - beginParameterName();
|
| - if (match(Token.THIS)) {
|
| - beginThisExpression();
|
| - expect(Token.THIS);
|
| - DartThisExpression thisExpression = done(DartThisExpression.get());
|
| - expect(Token.PERIOD);
|
| - return done(new DartPropertyAccess(thisExpression, parseIdentifier()));
|
| - }
|
| - return done(parseIdentifier());
|
| - }
|
| -
|
| - /**
|
| - * Validates that given {@link DartParameter}s have no default values, or marks existing default
|
| - * values with given {@link ErrorCode}.
|
| - */
|
| - private void validateNoDefaultParameterValues(List<DartParameter> parameters,
|
| - ErrorCode errorCode) {
|
| - for (int i = 0, size = parameters.size(); i < size; i++) {
|
| - DartParameter parameter = parameters.get(i);
|
| - DartExpression defaultExpr = parameter.getDefaultExpr();
|
| - if (defaultExpr != null) {
|
| - reportError(defaultExpr, errorCode);
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Parse an expression.
|
| - *
|
| - * <pre>
|
| - * expression
|
| - * : assignableExpression assignmentOperator expression
|
| - * | conditionalExpression cascadeSection*
|
| - * | throwExpression
|
| - * ;
|
| - *
|
| - * assignableExpression
|
| - * : primary (arguments* assignableSelector)+
|
| - * | SUPER assignableSelector
|
| - * | identifier
|
| - * ;
|
| - * </pre>
|
| - *
|
| - * @return an expression matching the {@code expression} production above
|
| - */
|
| - @VisibleForTesting
|
| - public DartExpression parseExpression() {
|
| - if (peek(0) == Token.THROW) {
|
| - return parseThrowExpression(true);
|
| - }
|
| - beginExpression();
|
| - if (looksLikeTopLevelKeyword() || peek(0).equals(Token.RBRACE)) {
|
| - // Allow recovery back to the top level.
|
| - reportErrorWithoutAdvancing(ParserErrorCode.UNEXPECTED_TOKEN);
|
| - return done(null);
|
| - }
|
| - DartExpression result = parseConditionalExpression();
|
| - Token token = peek(0);
|
| - if (token == Token.CASCADE) {
|
| - List<DartExpression> cascadeSections = new ArrayList<DartExpression>();
|
| - while (token == Token.CASCADE) {
|
| - beginExpression();
|
| - DartExpression section = parseCascadeSection();
|
| - done(section);
|
| - if (section != null) {
|
| - cascadeSections.add(section);
|
| - }
|
| - token = peek(0);
|
| - }
|
| - result = done(new DartCascadeExpression(result, cascadeSections));
|
| - } else if (token.isAssignmentOperator()) {
|
| - ensureAssignable(result);
|
| - consume(token);
|
| - int tokenOffset = ctx.getTokenLocation().getBegin();
|
| - result = done(new DartBinaryExpression(token, tokenOffset, result, parseExpression()));
|
| - } else {
|
| - done(null);
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - /**
|
| - * Parse an expression without a cascade.
|
| - *
|
| - * <pre>
|
| - * expressionWithoutCascade
|
| - * : assignableExpression assignmentOperator expressionWithoutCascade
|
| - * | conditionalExpression
|
| - * ;
|
| - * </pre>
|
| - *
|
| - * @return an expression matching the {@code expression} production above
|
| - */
|
| - private DartExpression parseExpressionWithoutCascade() {
|
| - if (peek(0) == Token.THROW) {
|
| - return parseThrowExpression(false);
|
| - }
|
| - beginExpression();
|
| - if (looksLikeTopLevelKeyword() || peek(0).equals(Token.RBRACE)) {
|
| - // Allow recovery back to the top level.
|
| - reportErrorWithoutAdvancing(ParserErrorCode.UNEXPECTED_TOKEN);
|
| - return done(null);
|
| - }
|
| - DartExpression result = parseConditionalExpression();
|
| - Token token = peek(0);
|
| - if (token.isAssignmentOperator()) {
|
| - ensureAssignable(result);
|
| - consume(token);
|
| - int tokenOffset = ctx.getTokenLocation().getBegin();
|
| - result = done(new DartBinaryExpression(token, tokenOffset, result, parseExpressionWithoutCascade()));
|
| - } else {
|
| - done(null);
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - /**
|
| - * Parse a cascade section.
|
| - * <pre>
|
| - * cascadeSection
|
| - * : CASCADE (cascadeSelector arguments*) (assignableSelector arguments*)* (assignmentOperator
|
| - * expressionWithoutCascade)?
|
| - * ;
|
| - *
|
| - * cascadeSelector
|
| - * : LBRACK expression RBRACK
|
| - * | identifier
|
| - * ;
|
| - * </pre>
|
| - *
|
| - * @return the expression representing the cascaded method invocation
|
| - */
|
| - private DartExpression parseCascadeSection() {
|
| - expect(Token.CASCADE);
|
| - DartExpression result = null;
|
| - DartIdentifier functionName = null;
|
| - if (peek(0) == Token.IDENTIFIER) {
|
| - functionName = parseIdentifier();
|
| - } else if (peek(0) == Token.LBRACK) {
|
| - consume(Token.LBRACK);
|
| - result = doneWithoutConsuming(new DartArrayAccess(result, true, parseExpression()));
|
| - expect(Token.RBRACK);
|
| - } else {
|
| - reportUnexpectedToken(position(), null, next());
|
| - return result;
|
| - }
|
| - if (peek(0) == Token.LPAREN) {
|
| - while (peek(0) == Token.LPAREN) {
|
| - if (functionName != null) {
|
| - result = doneWithoutConsuming(new DartMethodInvocation(result, result == null, functionName, parseArguments()));
|
| - functionName = null;
|
| - } else if (result == null) {
|
| - return null;
|
| - } else {
|
| - result = doneWithoutConsuming(new DartFunctionObjectInvocation(result, parseArguments()));
|
| - }
|
| - }
|
| - } else if (functionName != null) {
|
| - result = doneWithoutConsuming(new DartPropertyAccess(result, result == null, functionName));
|
| - }
|
| - boolean progress = true;
|
| - while (progress) {
|
| - progress = false;
|
| - DartExpression selector = tryParseAssignableSelector(result);
|
| - if (selector != null) {
|
| - result = selector;
|
| - progress = true;
|
| - while (peek(0) == Token.LPAREN) {
|
| - result = doneWithoutConsuming(new DartFunctionObjectInvocation(result, parseArguments()));
|
| - }
|
| - }
|
| - }
|
| - Token token = peek(0);
|
| - if (token.isAssignmentOperator()) {
|
| - ensureAssignable(result);
|
| - consume(token);
|
| - int tokenOffset = ctx.getTokenLocation().getBegin();
|
| - result = doneWithoutConsuming(new DartBinaryExpression(token, tokenOffset, result, parseExpressionWithoutCascade()));
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - /**
|
| - * expressionList
|
| - * : expression (',' expression)*
|
| - * ;
|
| - */
|
| - @Terminals(tokens={Token.COMMA})
|
| - private DartExpression parseExpressionList() {
|
| - beginExpressionList();
|
| - DartExpression result = parseExpression();
|
| - // Must keep in sync with @Terminals above
|
| - while (optional(Token.COMMA)) {
|
| - int tokenOffset = ctx.getTokenLocation().getBegin();
|
| - result = new DartBinaryExpression(Token.COMMA, tokenOffset, result, parseExpression());
|
| - if (match(Token.COMMA)) {
|
| - result = doneWithoutConsuming(result);
|
| - }
|
| - }
|
| - return done(result);
|
| - }
|
| -
|
| -
|
| - /**
|
| - * Parse a binary expression.
|
| - *
|
| - * <pre>
|
| - * logicalOrExpression
|
| - * : logicalAndExpression ('||' logicalAndExpression)*
|
| - * ;
|
| - *
|
| - * logicalAndExpression
|
| - * : bitwiseOrExpression ('&&' bitwiseOrExpression)*
|
| - * ;
|
| - *
|
| - * bitwiseOrExpression
|
| - * : bitwiseXorExpression ('|' bitwiseXorExpression)*
|
| - * ;
|
| - *
|
| - * bitwiseXorExpression
|
| - * : bitwiseAndExpression ('^' bitwiseAndExpression)*
|
| - * ;
|
| - *
|
| - * bitwiseAndExpression
|
| - * : equalityExpression ('&' equalityExpression)*
|
| - * ;
|
| - *
|
| - * equalityExpression
|
| - * : relationalExpression (equalityOperator relationalExpression)?
|
| - * ;
|
| - *
|
| - * relationalExpression
|
| - * : shiftExpression (isOperator type | relationalOperator shiftExpression)?
|
| - * ;
|
| - *
|
| - * shiftExpression
|
| - * : additiveExpression (shiftOperator additiveExpression)*
|
| - * ;
|
| - *
|
| - * additiveExpression
|
| - * : multiplicativeExpression (additiveOperator multiplicativeExpression)*
|
| - * ;
|
| - *
|
| - * multiplicativeExpression
|
| - * : unaryExpression (multiplicativeOperator unaryExpression)*
|
| - * ;
|
| - * </pre>
|
| - *
|
| - * @return an expression matching one of the productions above
|
| - */
|
| - private DartExpression parseBinaryExpression(int precedence) {
|
| - assert (precedence >= 4);
|
| - beginBinaryExpression();
|
| - DartExpression lastResult = parseUnaryExpression();
|
| - DartExpression result = lastResult;
|
| - for (int level = peekMaybeAS(0).getPrecedence(); level >= precedence; level--) {
|
| - while (peekMaybeAS(0).getPrecedence() == level) {
|
| - int prevPositionStart = ctx.getTokenLocation().getBegin();
|
| - int prevPositionEnd = ctx.getTokenLocation().getEnd();
|
| - Token token = nextMaybeAS();
|
| - int tokenOffset = ctx.getTokenLocation().getBegin();
|
| - if (lastResult instanceof DartSuperExpression
|
| - && (token == Token.AND || token == Token.OR)) {
|
| - reportErrorAtPosition(prevPositionStart, prevPositionEnd,
|
| - ParserErrorCode.SUPER_IS_NOT_VALID_AS_A_BOOLEAN_OPERAND);
|
| - }
|
| - if (token == Token.EQ_STRICT) {
|
| - reportError(tokenOffset, ParserErrorCode.DEPRECATED_STRICT_EQ);
|
| - }
|
| - if (token == Token.NE_STRICT) {
|
| - reportError(tokenOffset, ParserErrorCode.DEPRECATED_STRICT_NE);
|
| - }
|
| - DartExpression right;
|
| - if (token == Token.IS) {
|
| - beginTypeExpression();
|
| - if (optional(Token.NOT)) {
|
| - int notOffset = ctx.getTokenLocation().getBegin();
|
| - beginTypeExpression();
|
| - DartTypeExpression typeExpression = done(new DartTypeExpression(parseTypeAnnotation()));
|
| - right = done(new DartUnaryExpression(Token.NOT, notOffset, typeExpression, true));
|
| - } else {
|
| - right = done(new DartTypeExpression(parseTypeAnnotation()));
|
| - }
|
| - } else if (token == Token.AS) {
|
| - beginTypeExpression();
|
| - right = done(new DartTypeExpression(parseTypeAnnotation()));
|
| - } else {
|
| - right = parseBinaryExpression(level + 1);
|
| - }
|
| - if (right instanceof DartSuperExpression) {
|
| - reportError(position(), ParserErrorCode.SUPER_CANNOT_BE_USED_AS_THE_SECOND_OPERAND);
|
| - }
|
| -
|
| - lastResult = right;
|
| - result = doneWithoutConsuming(new DartBinaryExpression(token, tokenOffset, result, right));
|
| - if (token == Token.IS
|
| - || token == Token.AS
|
| - || token.isRelationalOperator()
|
| - || token.isEqualityOperator()) {
|
| - // The operations cannot be chained.
|
| - if (peekMaybeAS(0) == token) {
|
| - reportError(position(), ParserErrorCode.INVALID_OPERATOR_CHAINING,
|
| - token.toString().toLowerCase());
|
| - }
|
| - break;
|
| - }
|
| - }
|
| - }
|
| - done(null);
|
| - return result;
|
| - }
|
| -
|
| - /**
|
| - * Use this method where token "as" is expected to be used as built-in identifier.
|
| - *
|
| - * @return the {@link Token} at given position or {@link Token#AS}.
|
| - */
|
| - private Token peekMaybeAS(int n) {
|
| - Token token = ctx.peek(n);
|
| - String tokenString = ctx.peekTokenString(n);
|
| - if (token == Token.IDENTIFIER && "as".equals(tokenString)) {
|
| - return Token.AS;
|
| - }
|
| - return token;
|
| - }
|
| -
|
| - /**
|
| - * Use this method where token "as" is expected to be used as built-in identifier.
|
| - *
|
| - * @return the current {@link Token} or {@link Token#AS}.
|
| - */
|
| - private Token nextMaybeAS() {
|
| - ctx.advance();
|
| - Token token = ctx.getCurrentToken();
|
| - String tokenString = ctx.getTokenString();
|
| - if (token == Token.IDENTIFIER && "as".equals(tokenString)) {
|
| - return Token.AS;
|
| - }
|
| - return token;
|
| - }
|
| -
|
| - /**
|
| - * Parse the arguments passed to a function or method invocation.
|
| - *
|
| - * <pre>
|
| - * arguments
|
| - * : '(' argumentList? ')'
|
| - * ;
|
| - *
|
| - * argumentList
|
| - * : expression (',' expression)* (',' spreadArgument)?
|
| - * | spreadArgument
|
| - * ;
|
| - *
|
| - * spreadArgument
|
| - * : '...' expression
|
| - * ;
|
| - * </pre>
|
| - *
|
| - * @return a list of expressions containing the arguments to be passed
|
| - */
|
| - @Terminals(tokens={Token.RPAREN, Token.COMMA})
|
| - public List<DartExpression> parseArguments() {
|
| - List<DartExpression> arguments = new ArrayList<DartExpression>();
|
| - expect(Token.LPAREN);
|
| - // SEMICOLON is for error recovery
|
| - boolean namedArgumentParsed = false;
|
| - outer: while (!match(Token.RPAREN) && !match(Token.EOS) && !match(Token.SEMICOLON)) {
|
| - beginParameter();
|
| - // parse argument, may be named
|
| - DartExpression expression;
|
| - if (peek(1) == Token.COLON) {
|
| - DartIdentifier name = parseIdentifier();
|
| - expect(Token.COLON);
|
| - expression = new DartNamedExpression(name, parseExpression());
|
| - namedArgumentParsed = true;
|
| - } else {
|
| - expression = parseExpression();
|
| - if (namedArgumentParsed) {
|
| - reportError(expression, ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT);
|
| - }
|
| - }
|
| - done(expression);
|
| - // add argument, if parsed successfully
|
| - if (expression != null) {
|
| - arguments.add(expression);
|
| - }
|
| - // do we have more arguments?
|
| - switch(peek(0)) {
|
| - // Must keep in sync with @Terminals above
|
| - case COMMA:
|
| - if (peek(-1) == Token.COMMA) {
|
| - reportErrorWithoutAdvancing(ParserErrorCode.EXPECTED_EXPRESSION_AFTER_COMMA);
|
| - }
|
| - consume(Token.COMMA);
|
| - break;
|
| - // Must keep in sync with @Terminals above
|
| - case RPAREN:
|
| - break;
|
| - default:
|
| - Token actual = peek(0);
|
| - Set<Token> terminals = collectTerminalAnnotations();
|
| - if (terminals.contains(actual) || looksLikeTopLevelKeyword()) {
|
| - // Looks like a method already on the stack could use this token.
|
| - reportErrorWithoutAdvancing(ParserErrorCode.EXPECTED_COMMA_OR_RIGHT_PAREN);
|
| - break outer;
|
| - } else {
|
| - // Advance the parser state if no other method on the stack can use this token.
|
| - ctx.advance();
|
| - }
|
| - reportError(ctx.getTokenLocation().getEnd(),
|
| - ParserErrorCode.EXPECTED_COMMA_OR_RIGHT_PAREN, actual);
|
| - break;
|
| - }
|
| - }
|
| - if (peek(-1) == Token.COMMA) {
|
| - reportErrorWithoutAdvancing(ParserErrorCode.EXPECTED_EXPRESSION_AFTER_COMMA);
|
| - }
|
| - expectCloseParen();
|
| - return arguments;
|
| - }
|
| -
|
| - /**
|
| - * Parse a conditional expression.
|
| - *
|
| - * <pre>
|
| - * conditionalExpression
|
| - * : logicalOrExpression ('?' expression ':' expression)?
|
| - * ;
|
| - * </pre>
|
| - *
|
| - * @return an expression matching the {@code conditionalExpression} production
|
| - */
|
| - private DartExpression parseConditionalExpression() {
|
| - beginConditionalExpression();
|
| - DartExpression result = parseBinaryExpression(4);
|
| - if (result instanceof DartSuperExpression) {
|
| - reportError(position(), ParserErrorCode.SUPER_IS_NOT_VALID_ALONE_OR_AS_A_BOOLEAN_OPERAND);
|
| - }
|
| - if (peek(0) != Token.CONDITIONAL) {
|
| - return done(result);
|
| - }
|
| - consume(Token.CONDITIONAL);
|
| - DartExpression yes = parseExpressionWithoutCascade();
|
| - expect(Token.COLON);
|
| - DartExpression no = parseExpressionWithoutCascade();
|
| - return done(new DartConditional(result, yes, no));
|
| - }
|
| -
|
| - private boolean looksLikeStringInterpolation() {
|
| - int peekAhead = 0;
|
| - while (true) {
|
| - switch (peek(peekAhead++)) {
|
| - case STRING:
|
| - break;
|
| - case STRING_SEGMENT:
|
| - case STRING_LAST_SEGMENT:
|
| - case STRING_EMBED_EXP_START:
|
| - case STRING_EMBED_EXP_END:
|
| - return true;
|
| - default:
|
| - return false;
|
| - }
|
| - }
|
| - }
|
| - /**
|
| - * Pastes together adjacent strings. Re-uses the StringInterpolation
|
| - * node if there is more than one adjacent string.
|
| - */
|
| - private DartExpression parseStringWithPasting() {
|
| - List<DartExpression> expressions = new ArrayList<DartExpression>();
|
| - if (looksLikeStringInterpolation()) {
|
| - beginStringInterpolation();
|
| - } else {
|
| - beginLiteral();
|
| - }
|
| - DartExpression result = null;
|
| - boolean foundStringInterpolation = false;
|
| - do {
|
| - result = null;
|
| - switch(peek(0)) {
|
| - case STRING:
|
| - case STRING_SEGMENT:
|
| - case STRING_EMBED_EXP_START:
|
| - // another string is coming, glue it together.
|
| - result = parseString();
|
| - if (result != null) {
|
| - expressions.add(result);
|
| - }
|
| - if (result instanceof DartStringInterpolation) {
|
| - foundStringInterpolation = true;
|
| - }
|
| - break;
|
| - }
|
| - } while (result != null);
|
| -
|
| - if (expressions.size() == 0) {
|
| - return doneWithoutConsuming(null);
|
| - } else if (expressions.size() == 1) {
|
| - return done(expressions.get(0));
|
| - }
|
| -
|
| - if (foundStringInterpolation) {
|
| - DartStringInterpolationBuilder builder = new DartStringInterpolationBuilder();
|
| - // Create a new DartStringInterpolation object from the expressions.
|
| - boolean first = true;
|
| - for (DartExpression expr : expressions) {
|
| - if (!first) {
|
| - // pad between interpolations with a dummy expression
|
| - builder.addExpression(DartStringLiteral.get(""));
|
| - }
|
| - if (expr instanceof DartStringInterpolation) {
|
| - builder.addInterpolation((DartStringInterpolation)expr);
|
| - } else if (expr instanceof DartStringLiteral) {
|
| - builder.addString((DartStringLiteral)expr);
|
| - } else {
|
| - throw new InternalCompilerException("Expected String or StringInterpolation");
|
| - }
|
| - first = false;
|
| - }
|
| - return done(builder.buildInterpolation());
|
| - }
|
| -
|
| - // Synthesize a single String literal
|
| - List<DartStringLiteral> stringParts = new ArrayList<DartStringLiteral>();
|
| - StringBuilder builder = new StringBuilder();
|
| - for (DartExpression expr : expressions) {
|
| - DartStringLiteral stringPart = (DartStringLiteral)expr;
|
| - stringParts.add(stringPart);
|
| - builder.append(stringPart.getValue());
|
| - }
|
| - return done(DartStringLiteral.get(builder.toString(), stringParts));
|
| - }
|
| -
|
| - private DartExpression parseString() {
|
| - switch(peek(0)) {
|
| - case STRING: {
|
| - beginLiteral();
|
| - consume(Token.STRING);
|
| - return done(DartStringLiteral.get(ctx.getTokenString()));
|
| - }
|
| -
|
| - case STRING_SEGMENT:
|
| - case STRING_EMBED_EXP_START:
|
| - return parseStringInterpolation();
|
| -
|
| - default:
|
| - DartExpression expression = parseExpression();
|
| - reportError(position(), ParserErrorCode.EXPECTED_STRING_LITERAL);
|
| - return expression;
|
| - }
|
| - }
|
| -
|
| - private int skipStringLiteral(int offset) {
|
| - Token token = peek(offset);
|
| - while (token == Token.STRING || token == Token.STRING_SEGMENT || token == Token.STRING_EMBED_EXP_START) {
|
| - switch(token) {
|
| - case STRING:
|
| - offset = offset + 1;
|
| -
|
| - case STRING_SEGMENT:
|
| - case STRING_EMBED_EXP_START:
|
| - offset = skipStringInterpolation(offset);
|
| - }
|
| - token = peek(offset);
|
| - }
|
| - return offset;
|
| - }
|
| -
|
| - private int skipStringInterpolation(int offset) {
|
| - Token token = peek(offset);
|
| - if (token == Token.STRING_LAST_SEGMENT) {
|
| - return -1;
|
| - }
|
| - boolean inString = true;
|
| - while (inString) { // Iterate until we find the last string segment.
|
| - switch (token) {
|
| - case STRING_SEGMENT:
|
| - offset = offset + 1;
|
| - token = peek(offset);
|
| - break;
|
| - case STRING_LAST_SEGMENT:
|
| - offset = offset + 1;
|
| - token = peek(offset);
|
| - inString = false;
|
| - break;
|
| - case STRING_EMBED_EXP_START: {
|
| - offset = offset + 1;
|
| - token = peek(offset);
|
| - while (token != Token.EOS && token != Token.STRING_EMBED_EXP_END && token != Token.STRING_LAST_SEGMENT) {
|
| - if (token == Token.STRING || token == Token.STRING_SEGMENT || token == Token.STRING_EMBED_EXP_START) {
|
| - offset = skipStringLiteral(offset);
|
| - } else {
|
| - offset = offset + 1;
|
| - }
|
| - token = peek(offset);
|
| - }
|
| - if (token != Token.STRING_EMBED_EXP_END) {
|
| - inString = Token.STRING_LAST_SEGMENT != token;
|
| - }
|
| - break;
|
| - }
|
| - default:
|
| - inString = false;
|
| - break;
|
| - }
|
| - }
|
| - return offset;
|
| - }
|
| -
|
| - private DartStringLiteral parseUri() {
|
| - DartExpression str = parseStringWithPasting();
|
| - if (str instanceof DartStringLiteral) {
|
| - return (DartStringLiteral) str;
|
| - } else if (str != null) {
|
| - reportError(str, ParserErrorCode.URI_CANNOT_USE_INTERPOLATION);
|
| - DartStringLiteral result = DartStringLiteral.get("<invalid-uri>");
|
| - result.setSourceInfo(str.getSourceInfo());
|
| - return result;
|
| - } else {
|
| - expect(Token.STRING);
|
| - return DartStringLiteral.get(null);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Instances of the class {@code DepthCounter} represent the number of less than tokens that have
|
| - * not yet been matched.
|
| - */
|
| - private static class DepthCounter {
|
| - /**
|
| - * The number of less than tokens that have not yet been matched.
|
| - */
|
| - private int count = 0;
|
| -
|
| - /**
|
| - * Increment the number of less than tokens that have not yet been matched by the given amount
|
| - * (or decrement the count if the argument is negative).
|
| - *
|
| - * @param value the amount by which the count should be changed
|
| - * @return the count after it has been modified
|
| - */
|
| - public int add(int value) {
|
| - count += value;
|
| - return count;
|
| - }
|
| -
|
| - /**
|
| - * Return the number of less than tokens that have not yet been matched.
|
| - *
|
| - * @return the number of less than tokens that have not yet been matched
|
| - */
|
| - public int getCount() {
|
| - return count;
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Return the offset of the first token after a type name, or {@code -1} if the token at the given
|
| - * offset is not the start of a type name.
|
| - *
|
| - * @param offset the offset of the first token of the type name
|
| - * @return the offset of the first token after a type name
|
| - */
|
| - private int skipTypeName(int offset) {
|
| - return skipTypeName(offset, new DepthCounter());
|
| - }
|
| -
|
| - /**
|
| - * Return the offset of the first token after a type name, or {@code -1} if the token at the given
|
| - * offset is not the start of a type name.
|
| - *
|
| - * @param offset the offset of the first token of the type name
|
| - * @param depth the number of less-thans that have been encountered since the outer-most type name
|
| - * @return the offset of the first token after a type name
|
| - */
|
| - private int skipTypeArguments(int offset, DepthCounter depth) {
|
| - if (peek(offset) != Token.LT) {
|
| - return -1;
|
| - }
|
| - int oldDepth = depth.add(1);
|
| - offset = skipTypeName(offset + 1, depth);
|
| - if (offset < 0) {
|
| - return offset;
|
| - }
|
| - while (peek(offset) == Token.COMMA) {
|
| - offset = skipTypeName(offset + 1, depth);
|
| - if (offset < 0) {
|
| - return offset;
|
| - }
|
| - }
|
| - if (depth.getCount() < oldDepth) {
|
| - // We already passed the closing '>' for this list of type arguments
|
| - return offset;
|
| - }
|
| - if (peek(offset) == Token.GT) {
|
| - depth.add(-1);
|
| - return offset + 1;
|
| - } else if (peek(offset) == Token.SAR) {
|
| - depth.add(-2);
|
| - return offset + 1;
|
| - }
|
| - return -1;
|
| - }
|
| -
|
| - /**
|
| - * Return the offset of the first token after a type name, or {@code -1} if the token at the given
|
| - * offset is not the start of a type name.
|
| - *
|
| - * @param offset the offset of the first token of the type name
|
| - * @param depth the number of less-thans that have been encountered since the outer-most type name
|
| - * @return the offset of the first token after a type name
|
| - */
|
| - private int skipTypeName(int offset, DepthCounter depth) {
|
| - if (peek(offset) != Token.IDENTIFIER) {
|
| - return -1;
|
| - }
|
| - offset++;
|
| - if (peek(offset) == Token.PERIOD) {
|
| - offset++;
|
| - if (peek(offset) == Token.IDENTIFIER) {
|
| - // We tolerate a missing identifier in order to recover better
|
| - offset++;
|
| - }
|
| - }
|
| - if (peek(offset) == Token.LT) {
|
| - offset = skipTypeArguments(offset, depth);
|
| - }
|
| - return offset;
|
| - }
|
| -
|
| - /**
|
| - * Parse any literal that is not a function literal (those have already been
|
| - * handled before this method is called, so we don't need to handle them
|
| - * here).
|
| - *
|
| - * <pre>
|
| - * nonFunctionLiteral
|
| - * : NULL
|
| - * | TRUE
|
| - * | FALSE
|
| - * | HEX_NUMBER
|
| - * | RATIONAL_NUMBER
|
| - * | DOUBLE_NUMBER
|
| - * | STRING
|
| - * | mapLiteral
|
| - * | arrayLiteral
|
| - * ;
|
| - * </pre>
|
| - *
|
| - * @return an expression matching the {@code literal} production above
|
| - */
|
| - private DartExpression parseLiteral() {
|
| - beginLiteral();
|
| - if (PSEUDO_KEYWORDS_SET.contains(peek(0).getSyntax())) {
|
| - return done(parseIdentifier());
|
| - }
|
| - switch (peek(0)) {
|
| - case NULL_LITERAL: {
|
| - consume(Token.NULL_LITERAL);
|
| - return done(DartNullLiteral.get());
|
| - }
|
| -
|
| - case TRUE_LITERAL: {
|
| - consume(Token.TRUE_LITERAL);
|
| - return done(DartBooleanLiteral.get(true));
|
| - }
|
| -
|
| - case FALSE_LITERAL: {
|
| - consume(Token.FALSE_LITERAL);
|
| - return done(DartBooleanLiteral.get(false));
|
| - }
|
| -
|
| - case INTEGER_LITERAL: {
|
| - consume(Token.INTEGER_LITERAL);
|
| - String number = ctx.getTokenString();
|
| - return done(DartIntegerLiteral.get(new BigInteger(number)));
|
| - }
|
| -
|
| - case DOUBLE_LITERAL: {
|
| - consume(Token.DOUBLE_LITERAL);
|
| - String number = ctx.getTokenString();
|
| - return done(DartDoubleLiteral.get(Double.parseDouble(number)));
|
| - }
|
| -
|
| - case HEX_LITERAL: {
|
| - consume(Token.HEX_LITERAL);
|
| - String number = ctx.getTokenString();
|
| - return done(DartIntegerLiteral.get(new BigInteger(number, 16)));
|
| - }
|
| -
|
| - case LBRACE: {
|
| - return done(parseMapLiteral(false, null));
|
| - }
|
| -
|
| - case INDEX: {
|
| - expect(peek(0));
|
| - return done(new DartArrayLiteral(false, null, new ArrayList<DartExpression>()));
|
| - }
|
| -
|
| - case LBRACK: {
|
| - return done(parseArrayLiteral(false, null));
|
| - }
|
| -
|
| - case VOID:
|
| - // For better error recovery / code completion in the IDE, treat "void" as an identifier
|
| - // here and let it get reported as a resolution error.
|
| - case IDENTIFIER: {
|
| - return done(parseIdentifier());
|
| - }
|
| -
|
| - case SEMICOLON: {
|
| - // this is separate from the default case for better error recovery,
|
| - // leaving the semicolon for the caller to use for a statement boundary
|
| -
|
| - // we have to advance to get the proper position, but we want to leave
|
| - // the semicolon
|
| - startLookahead();
|
| - next();
|
| - reportUnexpectedToken(position(), null, Token.SEMICOLON);
|
| - rollback();
|
| - return done(new DartSyntheticErrorExpression(""));
|
| - }
|
| -
|
| - default: {
|
| - Token unexpected = peek(0);
|
| - String unexpectedString = ctx.getTokenString();
|
| - if (unexpectedString == null && unexpected != Token.EOS) {
|
| - unexpectedString = unexpected.getSyntax();
|
| - }
|
| -
|
| - // Don't eat tokens that could be used to successfully terminate a non-terminal
|
| - // further up the stack.
|
| - Set<Token> terminals = collectTerminalAnnotations();
|
| - if (!looksLikeTopLevelKeyword() && !terminals.contains(unexpected)) {
|
| - next();
|
| - }
|
| - reportUnexpectedToken(position(), null, unexpected);
|
| - StringBuilder tokenStr = new StringBuilder();
|
| - if (unexpectedString != null) {
|
| - tokenStr.append(unexpectedString);
|
| - }
|
| - // TODO(jat): should we eat additional tokens here for error recovery?
|
| - return done(new DartSyntheticErrorExpression(tokenStr.toString()));
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * mapLiteralEntry
|
| - * : STRING ':' expression
|
| - * ;
|
| - */
|
| - private DartMapLiteralEntry parseMapLiteralEntry() {
|
| - beginMapLiteralEntry();
|
| - // Parse the key.
|
| - DartExpression keyExpr = parseStringWithPasting();
|
| - if (keyExpr == null) {
|
| - return done(null);
|
| - }
|
| - // Parse the value.
|
| - DartExpression value;
|
| - if (expect(Token.COLON)) {
|
| - value = parseExpression();
|
| - } else {
|
| - value = doneWithoutConsuming(new DartSyntheticErrorExpression());
|
| - }
|
| - return done(new DartMapLiteralEntry(keyExpr, value));
|
| - }
|
| - private boolean looksLikeString() {
|
| - switch(peek(0)) {
|
| - case STRING:
|
| - case STRING_SEGMENT:
|
| - case STRING_EMBED_EXP_START:
|
| - return true;
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - /**
|
| - * <pre> mapLiteral : '{' (mapLiteralEntry (',' mapLiteralEntry)* ','?)? '}' ;
|
| - * </pre>
|
| - */
|
| - @Terminals(tokens={Token.RBRACE, Token.COMMA})
|
| - private DartExpression parseMapLiteral(boolean isConst, List<DartTypeNode> typeArguments) {
|
| - beginMapLiteral();
|
| - boolean foundOpenBrace = expect(Token.LBRACE);
|
| - boolean save = setAllowFunctionExpression(true);
|
| - List<DartMapLiteralEntry> entries = new ArrayList<DartMapLiteralEntry>();
|
| -
|
| - while (!match(Token.RBRACE) && !match(Token.EOS)) {
|
| - if (!looksLikeString()) {
|
| - ctx.advance();
|
| - reportError(position(), ParserErrorCode.EXPECTED_STRING_LITERAL_MAP_ENTRY_KEY);
|
| - if (peek(0) == Token.COMMA) {
|
| - // a common error is to put an empty entry in the list, allow it to
|
| - // recover.
|
| - continue;
|
| - } else {
|
| - break;
|
| - }
|
| - }
|
| - DartMapLiteralEntry entry = parseMapLiteralEntry();
|
| - if (entry != null) {
|
| - entries.add(entry);
|
| - }
|
| - Token nextToken = peek(0);
|
| - switch (nextToken) {
|
| - // Must keep in sync with @Terminals above
|
| - case COMMA:
|
| - consume(Token.COMMA);
|
| - break;
|
| - // Must keep in sync with @Terminals above
|
| - case RBRACE:
|
| - break;
|
| - default:
|
| - if (entry == null) {
|
| - Set<Token> terminals = collectTerminalAnnotations();
|
| - if (!terminals.contains(nextToken) && !looksLikeTopLevelKeyword()) {
|
| - if (entry == null) {
|
| - // Ensure the parser makes progress.
|
| - ctx.advance();
|
| - }
|
| - }
|
| - }
|
| - reportError(position(), ParserErrorCode.EXPECTED_COMMA_OR_RIGHT_BRACE);
|
| - break;
|
| - }
|
| - }
|
| -
|
| - expectCloseBrace(foundOpenBrace);
|
| - setAllowFunctionExpression(save);
|
| - return done(new DartMapLiteral(isConst, typeArguments, entries));
|
| - }
|
| -
|
| - /**
|
| - * // The array literal syntax doesn't allow elided elements, unlike
|
| - * // in ECMAScript.
|
| - *
|
| - * <pre>
|
| - * arrayLiteral
|
| - * : '[' expressionList? ']'
|
| - * ;
|
| - * </pre>
|
| - */
|
| - @Terminals(tokens={Token.RBRACK, Token.COMMA})
|
| - private DartExpression parseArrayLiteral(boolean isConst, List<DartTypeNode> typeArguments) {
|
| - beginArrayLiteral();
|
| - expect(Token.LBRACK);
|
| - boolean save = setAllowFunctionExpression(true);
|
| - List<DartExpression> exprs = new ArrayList<DartExpression>();
|
| - while (!match(Token.RBRACK) && !EOS()) {
|
| - exprs.add(parseExpression());
|
| - // Must keep in sync with @Terminals above
|
| - if (!optional(Token.COMMA)) {
|
| - break;
|
| - }
|
| - }
|
| - // Must keep in sync with @Terminals above
|
| - expect(Token.RBRACK);
|
| - setAllowFunctionExpression(save);
|
| - return done(new DartArrayLiteral(isConst, typeArguments, exprs));
|
| - }
|
| -
|
| - /**
|
| - * Parse a postfix expression.
|
| - *
|
| - * <pre>
|
| - * postfixExpression
|
| - * | assignableExpression postfixOperator
|
| - * : primary selector*
|
| - * ;
|
| - * </pre>
|
| - *
|
| - * @return an expression matching the {@code postfixExpression} production above
|
| - */
|
| - private DartExpression parsePostfixExpression() {
|
| - beginPostfixExpression();
|
| - DartExpression receiver = doneWithoutConsuming(parsePrimaryExpression());
|
| - DartExpression result = receiver;
|
| - do {
|
| - receiver = result;
|
| - result = doneWithoutConsuming(parseSelectorExpression(receiver));
|
| - } while (receiver != result);
|
| -
|
| - Token token = peek(0);
|
| - if (token.isCountOperator()) {
|
| - ensureAssignable(result);
|
| - consume(token);
|
| - int tokenOffset = ctx.getTokenLocation().getBegin();
|
| - result = doneWithoutConsuming(new DartUnaryExpression(token, tokenOffset, result, false));
|
| - }
|
| -
|
| - return done(result);
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * typeParameters? (arrayLiteral | mapLiteral)
|
| - * </pre>
|
| - *
|
| - * @param isConst <code>true</code> if a CONST expression
|
| - *
|
| - */
|
| - private DartExpression tryParseTypedCompoundLiteral(boolean isConst) {
|
| - beginLiteral();
|
| - List<DartTypeNode> typeArguments = parseTypeArgumentsOpt();
|
| - switch (peek(0)) {
|
| - case INDEX:
|
| - beginArrayLiteral();
|
| - consume(Token.INDEX);
|
| - return done(done(new DartArrayLiteral(isConst, typeArguments, new ArrayList<DartExpression>())));
|
| - case LBRACK:
|
| - return done(parseArrayLiteral(isConst, typeArguments));
|
| - case LBRACE:
|
| - return done(parseMapLiteral(isConst, typeArguments));
|
| - default:
|
| - if (typeArguments != null) {
|
| - rollback();
|
| - return null;
|
| - }
|
| -
|
| - }
|
| - // Doesn't look like a typed compound literal and no tokens consumed.
|
| - return done(null);
|
| - }
|
| -
|
| - private enum LastSeenNode {
|
| - NONE,
|
| - STRING,
|
| - EXPRESSION;
|
| - }
|
| -
|
| - private class DartStringInterpolationBuilder {
|
| -
|
| - private final List<DartStringLiteral> strings = new ArrayList<DartStringLiteral>();
|
| - private final List<DartExpression> expressions = new ArrayList<DartExpression>();
|
| - private LastSeenNode lastSeen = LastSeenNode.NONE;
|
| -
|
| - DartStringInterpolationBuilder() {
|
| - }
|
| -
|
| - void addString(DartStringLiteral string) {
|
| - if (lastSeen == LastSeenNode.STRING) {
|
| - expressions.add(new DartSyntheticErrorExpression());
|
| - }
|
| - strings.add(string);
|
| - lastSeen = LastSeenNode.STRING;
|
| - }
|
| -
|
| - void addExpression(DartExpression expression) {
|
| - switch (lastSeen) {
|
| - case EXPRESSION:
|
| - case NONE:
|
| - strings.add(DartStringLiteral.get(""));
|
| - break;
|
| - default:
|
| - break;
|
| - }
|
| - expressions.add(expression);
|
| - lastSeen = LastSeenNode.EXPRESSION;
|
| - }
|
| -
|
| - void addInterpolation(DartStringInterpolation interpolation) {
|
| - strings.addAll(interpolation.getStrings());
|
| - expressions.addAll(interpolation.getExpressions());
|
| - lastSeen = LastSeenNode.STRING;
|
| - }
|
| -
|
| - DartStringInterpolation buildInterpolation() {
|
| - if (strings.size() == expressions.size()) {
|
| - strings.add(DartStringLiteral.get(""));
|
| - }
|
| - return new DartStringInterpolation(strings, expressions);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Instances of the class {@code StringInterpolationParseError} represent the detection of an
|
| - * error that needs to be handled in an enclosing context.
|
| - */
|
| - private static class StringInterpolationParseError extends RuntimeException {
|
| - private static final long serialVersionUID = 1L;
|
| -
|
| - public StringInterpolationParseError() {
|
| - super();
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * string-interpolation
|
| - * : (STRING_SEGMENT? embedded-exp?)* STRING_LAST_SEGMENT
|
| - *
|
| - * embedded-exp
|
| - * : STRING_EMBED_EXP_START expression STRING_EMBED_EXP_END
|
| - * </pre>
|
| - */
|
| - private DartExpression parseStringInterpolation() {
|
| - // TODO(sigmund): generalize to parse string templates as well.
|
| - if (peek(0) == Token.STRING_LAST_SEGMENT) {
|
| - throw new InternalCompilerException("Invariant broken");
|
| - }
|
| - beginStringInterpolation();
|
| - DartStringInterpolationBuilder builder = new DartStringInterpolationBuilder();
|
| - boolean inString = true;
|
| - while (inString) { // Iterate until we find the last string segment.
|
| - switch (peek(0)) {
|
| - case STRING_SEGMENT: {
|
| - beginStringSegment();
|
| - consume(Token.STRING_SEGMENT);
|
| - builder.addString(done(DartStringLiteral.get(ctx.getTokenString())));
|
| - break;
|
| - }
|
| - case STRING_LAST_SEGMENT: {
|
| - beginStringSegment();
|
| - consume(Token.STRING_LAST_SEGMENT);
|
| - builder.addString(done(DartStringLiteral.get(ctx.getTokenString())));
|
| - inString = false;
|
| - break;
|
| - }
|
| - case STRING_EMBED_EXP_START: {
|
| - consume(Token.STRING_EMBED_EXP_START);
|
| - /*
|
| - * We check for ILLEGAL specifically here to give nicer error
|
| - * messages, and because the scanner doesn't generate a
|
| - * STRING_EMBED_EXP_END to match the START in the case of an ILLEGAL
|
| - * token.
|
| - */
|
| - if (peek(0) == Token.ILLEGAL) {
|
| - reportError(position(), ParserErrorCode.UNEXPECTED_TOKEN_IN_STRING_INTERPOLATION,
|
| - next());
|
| - builder.addExpression(new DartSyntheticErrorExpression(""));
|
| - break;
|
| - } else {
|
| - try {
|
| - builder.addExpression(parseExpression());
|
| - } catch (StringInterpolationParseError exception) {
|
| - if (peek(0) == Token.STRING_LAST_SEGMENT) {
|
| - break;
|
| - }
|
| - throw new InternalCompilerException("Invalid expression found in string interpolation");
|
| - }
|
| - }
|
| - Token lookAhead = peek(0);
|
| - String lookAheadString = getPeekTokenValue(0);
|
| - if (!expect(Token.STRING_EMBED_EXP_END)) {
|
| - String errorText = null;
|
| - if (lookAheadString != null && lookAheadString.length() > 0) {
|
| - errorText = lookAheadString;
|
| - } else if (lookAhead.getSyntax() != null && lookAhead.getSyntax().length() > 0) {
|
| - errorText = lookAhead.getSyntax();
|
| - }
|
| - if (errorText != null) {
|
| - builder.addExpression(new DartSyntheticErrorExpression(errorText));
|
| - }
|
| - inString = !(Token.STRING_LAST_SEGMENT == lookAhead);
|
| - }
|
| - break;
|
| - }
|
| - case EOS: {
|
| - reportError(position(), ParserErrorCode.INCOMPLETE_STRING_LITERAL);
|
| - inString = false;
|
| - break;
|
| - }
|
| - default: {
|
| - String errorText = getPeekTokenValue(0) != null && getPeekTokenValue(0).length() > 0
|
| - ? getPeekTokenValue(0) : null;
|
| - if(errorText != null) {
|
| - builder.addExpression(new DartSyntheticErrorExpression(getPeekTokenValue(0)));
|
| - }
|
| - reportError(position(), ParserErrorCode.UNEXPECTED_TOKEN_IN_STRING_INTERPOLATION,
|
| - next());
|
| - break;
|
| - }
|
| - }
|
| - }
|
| - return done(builder.buildInterpolation());
|
| - }
|
| -
|
| - /**
|
| - * Parse a return type, giving an error if the .
|
| - *
|
| - * @return a return type or null if the current text is not a return type
|
| - */
|
| - private DartTypeNode parseReturnType() {
|
| - if (peek(0) == Token.VOID) {
|
| - return parseVoidType();
|
| - } else {
|
| - return parseTypeAnnotation();
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Check if the current text could be a return type, and advance past it if so. The current
|
| - * position is unchanged if it is not a return type.
|
| - *
|
| - * NOTE: if the grammar is changed for what constitutes an acceptable return type, this method
|
| - * must be updated to match {@link #parseReturnType()}/etc.
|
| - *
|
| - * @return true if current text could be a return type, false otherwise
|
| - */
|
| - private boolean isReturnType() {
|
| - beginReturnType();
|
| - if (optional(Token.VOID)) {
|
| - done(null);
|
| - return true;
|
| - }
|
| - if (!optional(Token.IDENTIFIER)) {
|
| - rollback();
|
| - return false;
|
| - }
|
| - // handle prefixed identifiers
|
| - if (optional(Token.PERIOD)) {
|
| - if (!optional(Token.IDENTIFIER)) {
|
| - rollback();
|
| - return false;
|
| - }
|
| - }
|
| - // skip over type arguments if they are present
|
| - if (optional(Token.LT)) {
|
| - int count = 1;
|
| - while (count > 0) {
|
| - switch (next()) {
|
| - case EOS:
|
| - rollback();
|
| - return false;
|
| - case LT:
|
| - count++;
|
| - break;
|
| - case GT:
|
| - count--;
|
| - break;
|
| - case SHL:
|
| - count += 2;
|
| - break;
|
| - case SAR: // >>
|
| - count -= 2;
|
| - break;
|
| - case COMMA:
|
| - case IDENTIFIER:
|
| - // extends is a pseudokeyword, so shows up as IDENTIFIER
|
| - break;
|
| - default:
|
| - rollback();
|
| - return false;
|
| - }
|
| - }
|
| - if (count < 0) {
|
| - // if we had too many > (which can only be >> or >>>), can't be a return type
|
| - rollback();
|
| - return false;
|
| - }
|
| - }
|
| - done(null);
|
| - return true;
|
| - }
|
| -
|
| - /**
|
| - * Checks to see if the current text looks like a function expression:
|
| - *
|
| - * <pre>
|
| - * FUNCTION name? ( args ) < => | { >
|
| - * returnType name? ( args ) < => | { >
|
| - * name? ( args ) < => | { >
|
| - * </pre>
|
| - *
|
| - * The current position is unchanged on return.
|
| - *
|
| - * NOTE: if the grammar for function expressions changes, this method must be
|
| - * adapted to match the actual parsing code. It is acceptable for this method
|
| - * to return true when the source text does not actually represent a function
|
| - * expression (which would result in error messages assuming it was a function
|
| - * expression, but it must not do so when the source text would be correct if
|
| - * parsed as a non-function expression.
|
| - *
|
| - * @return true if the current text looks like a function expression, false
|
| - * otherwise
|
| - */
|
| - @VisibleForTesting
|
| - boolean looksLikeFunctionExpression() {
|
| - if (!allowFunctionExpression) {
|
| - return false;
|
| - }
|
| - return looksLikeFunctionDeclarationOrExpression();
|
| - }
|
| -
|
| - /**
|
| - * Check to see if the following tokens could be a function expression, and if so try and parse
|
| - * it as one.
|
| - *
|
| - * @return a function expression if found, or null (with no tokens consumed) if not
|
| - */
|
| - private DartExpression parseFunctionExpressionWithReturnType() {
|
| - beginFunctionLiteral();
|
| - DartIdentifier[] namePtr = new DartIdentifier[1];
|
| - DartFunction function = parseFunctionDeclarationOrExpression(namePtr, false);
|
| - if (function == null) {
|
| - rollback();
|
| - return null;
|
| - }
|
| - if (function != null && function.getReturnTypeNode() != null) {
|
| - reportError(function.getReturnTypeNode(), ParserErrorCode.DEPRECATED_FUNCTION_LITERAL);
|
| - } else if (namePtr[0] != null) {
|
| - reportError(namePtr[0], ParserErrorCode.DEPRECATED_FUNCTION_LITERAL);
|
| - }
|
| - return done(new DartFunctionExpression(namePtr[0], doneWithoutConsuming(function), false));
|
| - }
|
| -
|
| - /**
|
| - * Parse a function declaration or expression, including the body.
|
| - * <pre>
|
| - * ... | functionDeclaration functionBody
|
| - *
|
| - * functionDeclaration
|
| - * : returnType? identifier formalParameterList
|
| - * ;
|
| - *
|
| - * functionExpression
|
| - * : (returnType? identifier)? formalParameterList functionExpressionBody
|
| - * ;
|
| - *
|
| - * functionBody
|
| - * : '=>' expression ';'
|
| - * | block
|
| - * ;
|
| - *
|
| - * functionExpressionBody
|
| - * : '=>' expression
|
| - * | block
|
| - * ;
|
| - * </pre>
|
| - *
|
| - * @param namePtr out parameter - parsed function name stored in namePtr[0]
|
| - * @param isDeclaration true if this is a declaration (i.e. a name is required and a trailing
|
| - * semicolon is needed for arrow syntax
|
| - * @return a {@link DartFunction} containing the body of the function, or null
|
| - * if the next tokens cannot be parsed as a function declaration or expression
|
| - */
|
| - private DartFunction parseFunctionDeclarationOrExpression(DartIdentifier[] namePtr,
|
| - boolean isDeclaration) {
|
| - DartTypeNode returnType = null;
|
| - namePtr[0] = null;
|
| - if (optionalPseudoKeyword(STATIC_KEYWORD)) {
|
| - reportError(position(), ParserErrorCode.LOCAL_CANNOT_BE_STATIC);
|
| - }
|
| - switch (peek(0)) {
|
| - case LPAREN:
|
| - // no type or name, just the formal parameter list
|
| - break;
|
| - case IDENTIFIER:
|
| - if (peek(1) == Token.LPAREN) {
|
| - // if there is only one identifier, it must be the name
|
| - namePtr[0] = parseIdentifier();
|
| - break;
|
| - }
|
| - //$FALL-THROUGH$
|
| - case VOID:
|
| - returnType = parseReturnType();
|
| - if (peek(0) == Token.IDENTIFIER) {
|
| - namePtr[0] = parseIdentifier();
|
| - }
|
| - break;
|
| - default:
|
| - return null;
|
| - }
|
| - FormalParameters params = parseFormalParameterList();
|
| - int parametersCloseParen = ctx.getTokenLocation().getBegin();
|
| - DartBlock body = parseFunctionStatementBody(true, isDeclaration);
|
| - DartFunction function = new DartFunction(params.val, params.openParen,
|
| - params.optionalOpenOffset, params.optionalCloseOffset, parametersCloseParen, body,
|
| - returnType);
|
| - doneWithoutConsuming(function);
|
| - return function;
|
| - }
|
| -
|
| - /**
|
| - * Parse a primary expression.
|
| - *
|
| - * <pre>
|
| - * primary
|
| - * : THIS
|
| - * | SUPER assignableSelector
|
| - * | literal
|
| - * | identifier
|
| - * | NEW type ('.' identifier)? arguments
|
| - * | typeArguments? (arrayLiteral | mapLiteral)
|
| - * | CONST typeArguments? (arrayLiteral | mapLiteral)
|
| - * | CONST typeArguments? (arrayLiteral | mapLiteral)
|
| - * | CONST type ('.' identifier)? arguments
|
| - * | '(' expression ')'
|
| - * | string-interpolation
|
| - * | functionExpression
|
| - * ;
|
| - * </pre>
|
| - *
|
| - * @return an expression matching the {@code primary} production above
|
| - */
|
| - private DartExpression parsePrimaryExpression() {
|
| - if (looksLikeFunctionExpression()) {
|
| - return parseFunctionExpressionWithReturnType();
|
| - }
|
| - switch (peek(0)) {
|
| - case THIS: {
|
| - beginThisExpression();
|
| - consume(Token.THIS);
|
| - return done(DartThisExpression.get());
|
| - }
|
| -
|
| - case SUPER: {
|
| - beginSuperExpression();
|
| - consume(Token.SUPER);
|
| - return done(DartSuperExpression.get());
|
| - }
|
| -
|
| - case NEW: {
|
| - beginNewExpression(); // DartNewExpression
|
| - consume(Token.NEW);
|
| - return done(parseConstructorInvocation(false));
|
| - }
|
| -
|
| - case CONST: {
|
| - beginConstExpression();
|
| - consume(Token.CONST);
|
| -
|
| - DartExpression literal = tryParseTypedCompoundLiteral(true);
|
| - if (literal != null) {
|
| - return done(literal);
|
| - }
|
| - return done(parseConstructorInvocation(true));
|
| - }
|
| -
|
| - case LPAREN: {
|
| - beginParenthesizedExpression();
|
| - consume(Token.LPAREN);
|
| - beginExpression();
|
| - // inside parens, function blocks are allowed again
|
| - boolean save = setAllowFunctionExpression(true);
|
| - DartExpression expression = done(parseExpression());
|
| - setAllowFunctionExpression(save);
|
| - expectCloseParen();
|
| - return done(new DartParenthesizedExpression(expression));
|
| - }
|
| -
|
| - case LT: {
|
| - beginLiteral();
|
| - DartExpression literal = tryParseTypedCompoundLiteral(false);
|
| - if (literal == null) {
|
| - reportError(position(), ParserErrorCode.EXPECTED_ARRAY_OR_MAP_LITERAL);
|
| - }
|
| - return done(literal);
|
| - }
|
| -
|
| - case STRING:
|
| - case STRING_SEGMENT:
|
| - case STRING_EMBED_EXP_START: {
|
| - return parseStringWithPasting();
|
| - }
|
| -
|
| - case STRING_LAST_SEGMENT:
|
| - throw new StringInterpolationParseError();
|
| -
|
| - case CONDITIONAL:
|
| - return parseArgumentDefinitionTest();
|
| -
|
| - default: {
|
| - return parseLiteral();
|
| - }
|
| - }
|
| - }
|
| -
|
| - private DartExpression parseArgumentDefinitionTest() {
|
| - beginArgumentDefinitionTest();
|
| - int operatorOffset = position();
|
| - expect(Token.CONDITIONAL);
|
| - return done(new DartUnaryExpression(Token.CONDITIONAL, operatorOffset, parseIdentifier(), true));
|
| - }
|
| -
|
| - private DartExpression parseConstructorInvocation(boolean isConst) {
|
| - List<DartTypeNode> parts = new ArrayList<DartTypeNode>();
|
| - beginConstructor();
|
| - do {
|
| - beginConstructorNamePart();
|
| - parts.add(done(new DartTypeNode(parseIdentifier(), parseTypeArgumentsOpt())));
|
| - } while (optional(Token.PERIOD));
|
| - assert parts.size() > 0;
|
| -
|
| - DartNode constructor;
|
| - switch (parts.size()) {
|
| - case 1:
|
| - constructor = doneWithoutConsuming(parts.get(0));
|
| - break;
|
| -
|
| - case 2: {
|
| - // This case is ambiguous. It can either be prefix.Type or
|
| - // Type.namedConstructor.
|
| - boolean hasPrefix = false;
|
| - DartTypeNode part1 = parts.get(0);
|
| - DartTypeNode part2 = parts.get(1);
|
| - if (prefixes.contains(((DartIdentifier) part1.getIdentifier()).getName())) {
|
| - hasPrefix = true;
|
| - }
|
| - if (!part2.getTypeArguments().isEmpty()) {
|
| - // If the second part has type arguments, the first part must be a prefix.
|
| - // If it isn't a prefix, the resolver will complain.
|
| - hasPrefix = true;
|
| - }
|
| - if (hasPrefix) {
|
| - constructor = doneWithoutConsuming(toPrefixedType(parts));
|
| - } else {
|
| - // Named constructor.
|
| - DartIdentifier identifier = (DartIdentifier) part2.getIdentifier();
|
| - constructor = doneWithoutConsuming(new DartPropertyAccess(part1, identifier));
|
| - }
|
| - break;
|
| - }
|
| - default: {
|
| - // This case is unambiguous. It must be prefix.Type.namedConstructor.
|
| - if (parts.size() > 3) {
|
| - reportError(parts.get(3), ParserErrorCode.EXPECTED_LEFT_PAREN);
|
| - }
|
| - DartTypeNode typeNode = doneWithoutConsuming(toPrefixedType(parts));
|
| - DartIdentifier identifier = (DartIdentifier)parts.get(2).getIdentifier();
|
| - constructor = doneWithoutConsuming(new DartPropertyAccess(typeNode, identifier));
|
| - break;
|
| - }
|
| - }
|
| -
|
| - boolean save = setAllowFunctionExpression(true);
|
| - try {
|
| - List<DartExpression> args = parseArguments();
|
| - return done(new DartNewExpression(constructor, args, isConst));
|
| - } finally {
|
| - setAllowFunctionExpression(save);
|
| - }
|
| - }
|
| -
|
| - private DartTypeNode toPrefixedType(List<DartTypeNode> parts) {
|
| - DartIdentifier part1 = (DartIdentifier)parts.get(0).getIdentifier();
|
| - DartTypeNode part2 = parts.get(1);
|
| - DartIdentifier identifier = (DartIdentifier) part2.getIdentifier();
|
| - DartPropertyAccess access = doneWithoutConsuming(new DartPropertyAccess(part1, identifier));
|
| - return new DartTypeNode(access, part2.getTypeArguments());
|
| - }
|
| -
|
| - /**
|
| - * Parse a selector expression.
|
| - *
|
| - * <pre>
|
| - * selector
|
| - * : assignableSelector
|
| - * | arguments
|
| - * ;
|
| - * </pre>
|
| - *
|
| - * @return an expression matching the {@code selector} production above
|
| - */
|
| - private DartExpression parseSelectorExpression(DartExpression receiver) {
|
| - DartExpression expression = tryParseAssignableSelector(receiver);
|
| - if (expression != null) {
|
| - return expression;
|
| - }
|
| -
|
| - if (peek(0) == Token.LPAREN) {
|
| - beginSelectorExpression();
|
| - boolean save = setAllowFunctionExpression(true);
|
| - List<DartExpression> args = parseArguments();
|
| - setAllowFunctionExpression(save);
|
| - if (receiver instanceof DartIdentifier) {
|
| - return(done(new DartUnqualifiedInvocation((DartIdentifier) receiver, args)));
|
| - } else {
|
| - return(done(new DartFunctionObjectInvocation(receiver, args)));
|
| - }
|
| - }
|
| -
|
| - return receiver;
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * assignableSelector
|
| - * : '[' expression ']'
|
| - * | '.' identifier
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private DartExpression tryParseAssignableSelector(DartExpression receiver) {
|
| - switch (peek(0)) {
|
| - case PERIOD:
|
| - consume(Token.PERIOD);
|
| - switch (peek(0)) {
|
| - case SEMICOLON:
|
| - case RBRACE:
|
| - reportError(position(), ParserErrorCode.EXPECTED_IDENTIFIER);
|
| - DartIdentifier error = doneWithoutConsuming(new DartIdentifier(""));
|
| - return doneWithoutConsuming(new DartPropertyAccess(receiver, error));
|
| - }
|
| - // receiver.() = missing name
|
| - if (peek(0) == Token.LPAREN) {
|
| - reportUnexpectedToken(position(), Token.IDENTIFIER, peek(0));
|
| - DartIdentifier name;
|
| - {
|
| - beginIdentifier();
|
| - name = done(new DartSyntheticErrorIdentifier());
|
| - }
|
| - boolean save = setAllowFunctionExpression(true);
|
| - DartMethodInvocation expr = doneWithoutConsuming(new DartMethodInvocation(receiver,
|
| - false, name, parseArguments()));
|
| - setAllowFunctionExpression(save);
|
| - return expr;
|
| - }
|
| - // expect name
|
| - DartIdentifier name = parseIdentifier();
|
| - if (peek(0) == Token.LPAREN) {
|
| - boolean save = setAllowFunctionExpression(true);
|
| - DartMethodInvocation expr = doneWithoutConsuming(new DartMethodInvocation(receiver, false,
|
| - name, parseArguments()));
|
| - setAllowFunctionExpression(save);
|
| - return expr;
|
| - } else {
|
| - return doneWithoutConsuming(new DartPropertyAccess(receiver, name));
|
| - }
|
| -
|
| - case LBRACK:
|
| - consume(Token.LBRACK);
|
| - DartExpression key = parseExpression();
|
| - expect(Token.RBRACK);
|
| - return doneWithoutConsuming(new DartArrayAccess(receiver, key));
|
| -
|
| - default:
|
| - return null;
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * block
|
| - * : '{' statements deadCode* '}'
|
| - * ;
|
| - *
|
| - * statements
|
| - * : statement*
|
| - * ;
|
| - *
|
| - * deadCode
|
| - * : (normalCompletingStatement | abruptCompletingStatement)
|
| - * ;
|
| - * </pre>
|
| - */
|
| - @Terminals(tokens={Token.RBRACE})
|
| - private DartBlock parseBlock() {
|
| - if (isDietParse) {
|
| - expect(Token.LBRACE);
|
| - DartBlock emptyBlock = new DartBlock(new ArrayList<DartStatement>());
|
| - int nesting = 1;
|
| - while (nesting > 0) {
|
| - Token token = next();
|
| - switch (token) {
|
| - case LBRACE:
|
| - ++nesting;
|
| - break;
|
| - case RBRACE:
|
| - --nesting;
|
| - break;
|
| - case EOS:
|
| - return emptyBlock;
|
| - }
|
| - }
|
| - // Return an empty block so we don't generate unparseable code.
|
| - return emptyBlock;
|
| - } else {
|
| - Token nextToken = peek(0);
|
| - if (!nextToken.equals(Token.LBRACE)
|
| - && (looksLikeTopLevelKeyword() || nextToken.equals(Token.RBRACE))) {
|
| - beginBlock();
|
| - // Allow recovery back to the top level.
|
| - reportErrorWithoutAdvancing(ParserErrorCode.UNEXPECTED_TOKEN);
|
| - return done(new DartBlock(new ArrayList<DartStatement>()));
|
| - }
|
| - beginBlock();
|
| - List<DartStatement> statements = new ArrayList<DartStatement>();
|
| - boolean foundOpenBrace = expect(Token.LBRACE);
|
| -
|
| - while (!match(Token.RBRACE) && !EOS()) {
|
| - if (looksLikeTopLevelKeyword()) {
|
| - reportErrorWithoutAdvancing(ParserErrorCode.UNEXPECTED_TOKEN);
|
| - break;
|
| - }
|
| - int startPosition = position();
|
| - DartStatement newStatement = parseStatement();
|
| - if (newStatement == null) {
|
| - break;
|
| - }
|
| - if (startPosition == position()) {
|
| - // The parser is not making progress.
|
| - Set<Token> terminals = this.collectTerminalAnnotations();
|
| - if (terminals.contains(peek(0))) {
|
| - // bail out of the block
|
| - break;
|
| - }
|
| - reportUnexpectedToken(position(), null, next());
|
| - }
|
| - statements.add(newStatement);
|
| - }
|
| - expectCloseBrace(foundOpenBrace);
|
| - return done(new DartBlock(statements));
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Parse a function statement body.
|
| - *
|
| - * <pre>
|
| - * functionStatementBody
|
| - * : '=>' expression ';'
|
| - * | block
|
| - * </pre>
|
| - *
|
| - * @param requireSemicolonForArrow true if a semicolon is required after an arrow expression
|
| - * @return {@link DartBlock} instance containing function body
|
| - */
|
| - private DartBlock parseFunctionStatementBody(boolean allowBody, boolean requireSemicolonForArrow) {
|
| - // A break inside a function body should have nothing to do with a loop in
|
| - // the code surrounding the definition.
|
| - boolean oldInLoopStatement = inLoopStatement;
|
| - boolean oldInCaseStatement = inCaseStatement;
|
| - inLoopStatement = inCaseStatement = false;
|
| - try {
|
| - DartBlock result;
|
| - if (isDietParse) {
|
| - result = dietParseFunctionStatementBody();
|
| - } else {
|
| - beginFunctionStatementBody();
|
| - if (optional(Token.SEMICOLON)) {
|
| - if (allowBody) {
|
| - reportError(position(), ParserErrorCode.EXPECTED_FUNCTION_STATEMENT_BODY);
|
| - }
|
| - result = done(null);
|
| - } else if (optional(Token.ARROW)) {
|
| - DartExpression expr = parseExpression();
|
| - if (expr == null) {
|
| - expr = new DartSyntheticErrorExpression();
|
| - }
|
| - if (requireSemicolonForArrow) {
|
| - expect(Token.SEMICOLON);
|
| - }
|
| - result = done(makeReturnBlock(expr));
|
| - } else {
|
| - result = done(parseBlock());
|
| - }
|
| - }
|
| - if (!allowBody && result != null) {
|
| - reportError(result, ParserErrorCode.EXTERNAL_METHOD_BODY);
|
| - }
|
| - return result;
|
| - } finally {
|
| - inLoopStatement = oldInLoopStatement;
|
| - inCaseStatement = oldInCaseStatement;
|
| - }
|
| - }
|
| -
|
| - private DartBlock dietParseFunctionStatementBody() {
|
| - DartBlock emptyBlock = new DartBlock(new ArrayList<DartStatement>());
|
| - if (optional(Token.ARROW)) {
|
| - while (true) {
|
| - Token token = next();
|
| - if (token == Token.SEMICOLON) {
|
| - break;
|
| - }
|
| - }
|
| - } else {
|
| - if (!peek(0).equals(Token.LBRACE) && looksLikeTopLevelKeyword()) {
|
| - // Allow recovery back to the top level.
|
| - reportErrorWithoutAdvancing(ParserErrorCode.UNEXPECTED_TOKEN);
|
| - return done(emptyBlock);
|
| - }
|
| - expect(Token.LBRACE);
|
| - int nesting = 1;
|
| - while (nesting > 0) {
|
| - Token token = next();
|
| - switch (token) {
|
| - case LBRACE:
|
| - ++nesting;
|
| - break;
|
| - case RBRACE:
|
| - --nesting;
|
| - break;
|
| - case EOS:
|
| - return emptyBlock;
|
| - }
|
| - }
|
| - }
|
| - // Return an empty block so we don't generate unparseable code.
|
| - return emptyBlock;
|
| - }
|
| -
|
| - /**
|
| - * Create a block containing a single return statement.
|
| - *
|
| - * @param returnVal return value expression
|
| - * @return block containing a single return statement
|
| - */
|
| - private DartBlock makeReturnBlock(DartExpression returnVal) {
|
| - return new DartReturnBlock(returnVal);
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * initializedVariableDeclaration
|
| - * : constVarOrType initializedIdentifierList
|
| - * ;
|
| - *
|
| - * initializedIdentifierList
|
| - * : initializedIdentifier (',' initializedIdentifier)*
|
| - * ;
|
| - *
|
| - * initializedIdentifier
|
| - * : IDENTIFIER ('=' assignmentExpression)?
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private List<DartVariable> parseInitializedVariableList() {
|
| - List<DartVariable> idents = new ArrayList<DartVariable>();
|
| - do {
|
| - beginVariableDeclaration();
|
| - List<DartAnnotation> metadata = parseMetadata();
|
| - DartIdentifier name = parseIdentifier();
|
| - DartExpression value = null;
|
| - if (isParsingInterface) {
|
| - expect(Token.ASSIGN);
|
| - value = parseExpression();
|
| - } else if (optional(Token.ASSIGN)) {
|
| - value = parseExpression();
|
| - }
|
| - DartVariable variable = done(new DartVariable(name, value));
|
| - setMetadata(variable, metadata);
|
| - idents.add(variable);
|
| - } while (optional(Token.COMMA));
|
| -
|
| - return idents;
|
| - }
|
| -
|
| - private DartAssertStatement parseAssertStatement() {
|
| - beginAssertStatement();
|
| - expect(Token.ASSERT);
|
| - expect(Token.LPAREN);
|
| - DartExpression condition = parseExpression();
|
| - expectCloseParen();
|
| - expectStatmentTerminator();
|
| - return done(new DartAssertStatement(condition));
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * abruptCompletingStatement
|
| - * : BREAK identifier? ';'
|
| - * | CONTINUE identifier? ';'
|
| - * | RETURN expression? ';'
|
| - * | THROW expression? ';'
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private DartBreakStatement parseBreakStatement() {
|
| - beginBreakStatement();
|
| - expect(Token.BREAK);
|
| - DartIdentifier label = null;
|
| - if (match(Token.IDENTIFIER)) {
|
| - label = parseIdentifier();
|
| - } else if (!inLoopStatement && !inCaseStatement) {
|
| - // The validation of matching of labels to break statements is done later.
|
| - reportErrorWithoutAdvancing(ParserErrorCode.BREAK_OUTSIDE_OF_LOOP);
|
| - }
|
| - expectStatmentTerminator();
|
| - return done(new DartBreakStatement(label));
|
| - }
|
| -
|
| - private DartContinueStatement parseContinueStatement() {
|
| - beginContinueStatement();
|
| - expect(Token.CONTINUE);
|
| - DartIdentifier label = null;
|
| - if (!inLoopStatement && !inCaseStatement) {
|
| - reportErrorWithoutAdvancing(ParserErrorCode.CONTINUE_OUTSIDE_OF_LOOP);
|
| - }
|
| - if (peek(0) == Token.IDENTIFIER) {
|
| - label = parseIdentifier();
|
| - } else if (!inLoopStatement && inCaseStatement) {
|
| - reportErrorWithoutAdvancing(ParserErrorCode.CONTINUE_IN_CASE_MUST_HAVE_LABEL);
|
| - }
|
| - expectStatmentTerminator();
|
| - return done(new DartContinueStatement(label));
|
| - }
|
| -
|
| - private DartReturnStatement parseReturnStatement() {
|
| - beginReturnStatement();
|
| - expect(Token.RETURN);
|
| - DartExpression value = null;
|
| - if (peek(0) != Token.SEMICOLON) {
|
| - value = parseExpression();
|
| - }
|
| - expectStatmentTerminator();
|
| - return done(new DartReturnStatement(value));
|
| - }
|
| -
|
| - private DartThrowExpression parseThrowExpression(boolean allowCascade) {
|
| - beginThrowExpression();
|
| - expect(Token.THROW);
|
| - DartExpression exception = null;
|
| - if (peek(0) != Token.SEMICOLON && peek(0) != Token.RPAREN) {
|
| - if (allowCascade) {
|
| - exception = parseExpression();
|
| - } else {
|
| - exception = parseExpressionWithoutCascade();
|
| - }
|
| - }
|
| - return done(new DartThrowExpression(exception));
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * statement
|
| - * : label* nonLabelledStatement
|
| - * ;
|
| - *
|
| - * label
|
| - * : identifier ':'
|
| - * ;
|
| - * </pre>
|
| - *
|
| - * @return a {@link DartStatement}
|
| - */
|
| - @VisibleForTesting
|
| - public DartStatement parseStatement() {
|
| - List<DartIdentifier> labels = new ArrayList<DartIdentifier>();
|
| - while (peek(0) == Token.IDENTIFIER && peek(1) == Token.COLON) {
|
| - beginLabel();
|
| - labels.add(parseIdentifier());
|
| - expect(Token.COLON);
|
| - }
|
| - List<DartAnnotation> metadata = parseMetadata();
|
| - DartStatement statement = parseNonLabelledStatement();
|
| - if (!metadata.isEmpty() && statement instanceof DartVariableStatement) {
|
| - DartVariableStatement variableStatement = (DartVariableStatement) statement;
|
| - if (!variableStatement.getVariables().isEmpty()) {
|
| - setMetadata(variableStatement.getVariables().get(0), metadata);
|
| - }
|
| - }
|
| - for (int i = labels.size() - 1; i >= 0; i--) {
|
| - statement = done(new DartLabel(labels.get(i), statement));
|
| - }
|
| - return statement;
|
| - }
|
| -
|
| - private boolean isFunctionExpression(DartStatement statement) {
|
| - if (!(statement instanceof DartExprStmt)) {
|
| - return false;
|
| - }
|
| - DartExpression expression = ((DartExprStmt) statement).getExpression();
|
| - if (!(expression instanceof DartFunctionExpression)) {
|
| - return false;
|
| - }
|
| - return ((DartFunctionExpression) expression).getName() == null;
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * normalCompletingStatement
|
| - * : functionStatement
|
| - * | initializedVariableDeclaration ';'
|
| - * | simpleStatement
|
| - * ;
|
| - *
|
| - * functionStatement
|
| - * : typeOrFunction identifier formalParameterList block
|
| - * ;
|
| - * ;
|
| - *
|
| - * simpleStatement
|
| - * : ('{')=> block // Guard to break tie with map literal.
|
| - * | expression? ';'
|
| - * | tryStatement
|
| - * | ASSERT '(' conditionalExpression ')' ';'
|
| - * | abruptCompletingStatement
|
| - * ;
|
| - * </pre>
|
| - */
|
| - // TODO(zundel): Possibly we could use Token.IDENTIFIER too, but it is used
|
| - // in so many places, it might make recovery worse rather than better.
|
| - @Terminals(tokens={Token.IF, Token.SWITCH, Token.WHILE, Token.DO, Token.FOR,
|
| - Token.VAR, Token.FINAL, Token.CONTINUE, Token.BREAK, Token.RETHROW,
|
| - Token.RETURN, Token.THROW, Token.TRY, Token.SEMICOLON })
|
| - private DartStatement parseNonLabelledStatement() {
|
| - // Try to parse as function declaration.
|
| - if (looksLikeFunctionDeclarationOrExpression()) {
|
| - ctx.begin();
|
| - DartStatement functionDeclaration = parseFunctionDeclaration();
|
| - // If "null", then we tried to parse, but found that this is not function declaration.
|
| - // So, parsing was rolled back and we can try to parse it as expression.
|
| - if (functionDeclaration != null) {
|
| - if (!isFunctionExpression(functionDeclaration)) {
|
| - ctx.done(null);
|
| - return functionDeclaration;
|
| - }
|
| - ctx.rollback();
|
| - } else {
|
| - ctx.done(null);
|
| - }
|
| - }
|
| - // Check possible statement kind.
|
| - switch (peek(0)) {
|
| - case ASSERT:
|
| - return parseAssertStatement();
|
| -
|
| - case IF:
|
| - return parseIfStatement();
|
| -
|
| - case SWITCH:
|
| - return parseSwitchStatement();
|
| -
|
| - case WHILE:
|
| - return parseWhileStatement();
|
| -
|
| - case DO:
|
| - return parseDoWhileStatement();
|
| -
|
| - case FOR:
|
| - return parseForStatement();
|
| -
|
| - case VAR: {
|
| - beginVarDeclaration();
|
| - consume(Token.VAR);
|
| - List<DartVariable> vars = parseInitializedVariableList();
|
| - expectStatmentTerminator();
|
| - return done(new DartVariableStatement(vars, null));
|
| - }
|
| -
|
| - case FINAL: {
|
| - beginFinalDeclaration();
|
| - consume(peek(0));
|
| - DartTypeNode type = null;
|
| - if (peek(1) == Token.IDENTIFIER || peek(1) == Token.LT || peek(1) == Token.PERIOD) {
|
| - // We know we have a type.
|
| - type = parseTypeAnnotation();
|
| - }
|
| - List<DartVariable> vars = parseInitializedVariableList();
|
| - expectStatmentTerminator();
|
| - return done(new DartVariableStatement(vars, type, Modifiers.NONE.makeFinal()));
|
| - }
|
| -
|
| - case LBRACE:
|
| - Token token = peek(1);
|
| - if (token == Token.STRING || token == Token.STRING_SEGMENT || token == Token.STRING_EMBED_EXP_START) {
|
| - int offset = skipStringLiteral(1);
|
| - if (peek(offset) == Token.COLON) {
|
| - DartStatement statement = parseExpressionStatement();
|
| - if (statement instanceof DartExprStmt
|
| - && ((DartExprStmt) statement).getExpression() instanceof DartMapLiteral) {
|
| - reportError(
|
| - ((DartExprStmt) statement).getExpression(),
|
| - ParserErrorCode.NON_CONST_MAP_LITERAL_STATEMENT);
|
| - }
|
| - return statement;
|
| - }
|
| - }
|
| - return parseBlock();
|
| -
|
| - case CONTINUE:
|
| - return parseContinueStatement();
|
| -
|
| - case BREAK:
|
| - return parseBreakStatement();
|
| -
|
| - case RETURN:
|
| - return parseReturnStatement();
|
| -
|
| - case THROW:
|
| - return parseExpressionStatement();
|
| -
|
| - case RETHROW:
|
| - consume(Token.RETHROW);
|
| - beginExpressionStatement();
|
| - return done(new DartExprStmt(new DartThrowExpression(null)));
|
| -
|
| - case TRY:
|
| - return parseTryStatement();
|
| -
|
| - case SEMICOLON:
|
| - beginEmptyStatement();
|
| - consume(Token.SEMICOLON);
|
| - return done(new DartEmptyStatement());
|
| -
|
| - case CONST:
|
| - // Check to see whether this is a variable declaration. If not, then default to parsing an
|
| - // expression statement.
|
| - int offset = skipTypeName(1);
|
| - if (offset > 1 && (peek(offset) == Token.IDENTIFIER || (offset == 2
|
| - && (peek(offset) == Token.ASSIGN || peek(offset) == Token.COMMA || peek(offset) == Token.SEMICOLON)))) {
|
| - boolean hasType = peek(offset) == Token.IDENTIFIER;
|
| - beginVariableDeclaration();
|
| - next();
|
| - DartTypeNode type = null;
|
| - if (hasType) {
|
| - type = parseTypeAnnotation();
|
| - }
|
| - List<DartVariable> vars = parseInitializedVariableList();
|
| - expect(Token.SEMICOLON);
|
| - return done(new DartVariableStatement(vars, type, Modifiers.NONE.makeConstant().makeFinal()));
|
| - }
|
| - break;
|
| -
|
| - case IDENTIFIER:
|
| - // We have already eliminated function declarations earlier, so check for:
|
| - // a) variable declarations;
|
| - // b) beginning of function literal invocation.
|
| - if (peek(1) == Token.LT || peekMaybeAS(1) == Token.IDENTIFIER
|
| - || (peek(1) == Token.PERIOD && peek(2) == Token.IDENTIFIER)) {
|
| - beginTypeFunctionOrVariable();
|
| - DartTypeNode type = tryTypeAnnotation();
|
| - if (type != null && peekMaybeAS(0) == Token.IDENTIFIER) {
|
| - List<DartVariable> vars = parseInitializedVariableList();
|
| - if (optional(Token.SEMICOLON)) {
|
| - return done(new DartVariableStatement(vars, type));
|
| - } else if (peek(0) == Token.LPAREN) {
|
| - // Probably a function object invocation.
|
| - rollback();
|
| - } else {
|
| - //reportError(position(), ParserErrorCode.EXPECTED_SEMICOLON);
|
| - expectStatmentTerminator();
|
| - return done(new DartVariableStatement(vars, type));
|
| - }
|
| - } else {
|
| - rollback();
|
| - }
|
| - }
|
| - break;
|
| - }
|
| - return parseExpressionStatement();
|
| - }
|
| -
|
| - /**
|
| - * Check if succeeding tokens look like a function declaration - the parser state is unchanged
|
| - * upon return.
|
| - *
|
| - * See {@link #parseFunctionDeclaration()}.
|
| - *
|
| - * @return true if the following tokens should be parsed as a function definition
|
| - */
|
| - private boolean looksLikeFunctionDeclarationOrExpression() {
|
| - beginMethodName();
|
| - try {
|
| - optionalPseudoKeyword(STATIC_KEYWORD);
|
| - if (peek(0) == Token.IDENTIFIER && peek(1) == Token.LPAREN) {
|
| - // just a name, no return type
|
| - consume(Token.IDENTIFIER);
|
| - } else if (isReturnType()) {
|
| - if (!optional(Token.IDENTIFIER)) {
|
| - // return types must be followed by a function name
|
| - return false;
|
| - }
|
| - }
|
| - // start of parameter list
|
| - if (!optional(Token.LPAREN)) {
|
| - return false;
|
| - }
|
| - // if it looks as "(Type name, ....)" then it may be function expression
|
| - boolean hasTwoIdentifiersComma;
|
| - {
|
| - int nameOffset = skipTypeName(0);
|
| - hasTwoIdentifiersComma = nameOffset != -1 && peek(nameOffset + 0) == Token.IDENTIFIER
|
| - && peek(nameOffset + 1) == Token.COMMA;
|
| - }
|
| - // find matching parenthesis
|
| - int count = 1;
|
| - while (count != 0) {
|
| - switch (next()) {
|
| - case EOS:
|
| - return false;
|
| - case LPAREN:
|
| - count++;
|
| - break;
|
| - case RPAREN:
|
| - count--;
|
| - break;
|
| - }
|
| - }
|
| - return (peek(0) == Token.ARROW || peek(0) == Token.LBRACE) || hasTwoIdentifiersComma;
|
| - } finally {
|
| - rollback();
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Parse a function declaration.
|
| - *
|
| - * <pre>
|
| - * nonLabelledStatement : ...
|
| - * | functionDeclaration functionBody
|
| - *
|
| - * functionDeclaration
|
| - * : FUNCTION identifier formalParameterList
|
| - * { legacy($start, "deprecated 'function' keyword"); }
|
| - * | returnType error=FUNCTION identifier? formalParameterList
|
| - * { legacy($error, "deprecated 'function' keyword"); }
|
| - * | returnType? identifier formalParameterList
|
| - * ;
|
| - * </pre>
|
| - *
|
| - * @return a {@link DartStatement} representing the function declaration or <code>null</code> if
|
| - * code ends with function invocation, so this is not function declaration.
|
| - */
|
| - private DartStatement parseFunctionDeclaration() {
|
| - beginFunctionDeclaration();
|
| - DartIdentifier[] namePtr = new DartIdentifier[1];
|
| - DartFunction function = parseFunctionDeclarationOrExpression(namePtr, true);
|
| - if (function.getBody() instanceof DartReturnBlock || peek(0) != Token.LPAREN) {
|
| - return done(new DartExprStmt(doneWithoutConsuming(new DartFunctionExpression(namePtr[0],
|
| - doneWithoutConsuming(function),
|
| - true))));
|
| - } else {
|
| - rollback();
|
| - return null;
|
| - }
|
| - }
|
| -
|
| - private DartStatement parseExpressionStatement() {
|
| - beginExpressionStatement();
|
| - DartExpression expression = parseExpression();
|
| - expectStatmentTerminator();
|
| -
|
| - return done(new DartExprStmt(expression));
|
| - }
|
| -
|
| - /**
|
| - * Expect a close paren, reporting an error and consuming tokens until a
|
| - * plausible continuation is found if it isn't present.
|
| - */
|
| - private void expectCloseParen() {
|
| - int parenCount = 1;
|
| - Token nextToken = peek(0);
|
| - switch (nextToken) {
|
| - case RPAREN:
|
| - expect(Token.RPAREN);
|
| - return;
|
| -
|
| - case EOS:
|
| - case LBRACE:
|
| - case SEMICOLON:
|
| - reportError(position(), ParserErrorCode.EXPECTED_TOKEN, Token.RPAREN.getSyntax(),
|
| - nextToken.getSyntax());
|
| - return;
|
| -
|
| - case LPAREN:
|
| - ++parenCount;
|
| - //$FALL-THROUGH$
|
| - default:
|
| - reportError(position(), ParserErrorCode.EXPECTED_TOKEN, Token.RPAREN.getSyntax(),
|
| - nextToken.getSyntax());
|
| - Set<Token> terminals = this.collectTerminalAnnotations();
|
| - if (terminals.contains(nextToken) || looksLikeTopLevelKeyword()) {
|
| - return;
|
| - }
|
| - break;
|
| - }
|
| -
|
| - // eat tokens until we get a close paren or a plausible terminator (which
|
| - // is not consumed)
|
| - while (parenCount > 0) {
|
| - switch (peek(0)) {
|
| - case RPAREN:
|
| - expect(Token.RPAREN);
|
| - --parenCount;
|
| - break;
|
| -
|
| - case LPAREN:
|
| - expect(Token.LPAREN);
|
| - ++parenCount;
|
| - break;
|
| -
|
| - case EOS:
|
| - reportErrorWithoutAdvancing(ParserErrorCode.UNEXPECTED_TOKEN);
|
| - return;
|
| -
|
| - case LBRACE:
|
| - case SEMICOLON:
|
| - return;
|
| -
|
| - default:
|
| - next();
|
| - break;
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Expect a close brace, reporting an error and consuming tokens until a
|
| - * plausible continuation is found if it isn't present.
|
| - */
|
| - private void expectCloseBrace(boolean foundOpenBrace) {
|
| - // If a top level keyword is seen, bail out to recover.
|
| - if (looksLikeTopLevelKeyword()) {
|
| - reportUnexpectedToken(position(), Token.RBRACE, peek(0));
|
| - return;
|
| - }
|
| -
|
| - int braceCount = 0;
|
| - if (foundOpenBrace) {
|
| - braceCount++;
|
| - }
|
| - Token nextToken = peek(0);
|
| - if (expect(Token.RBRACE)) {
|
| - return;
|
| - }
|
| - if (nextToken == Token.LBRACE) {
|
| - braceCount++;
|
| - }
|
| -
|
| - // eat tokens until we get a matching close brace or end of stream
|
| - while (braceCount > 0) {
|
| - if (looksLikeTopLevelKeyword()) {
|
| - return;
|
| - }
|
| - switch (next()) {
|
| - case RBRACE:
|
| - braceCount--;
|
| - break;
|
| -
|
| - case LBRACE:
|
| - braceCount++;
|
| - break;
|
| -
|
| - case EOS:
|
| - return;
|
| - }
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Collect plausible statement tokens and return a synthetic error statement
|
| - * containing them.
|
| - * <p>
|
| - * Note that this is a crude heuristic that needs to be improved for better
|
| - * error recovery.
|
| - *
|
| - * @return a {@link DartSyntheticErrorStatement}
|
| - */
|
| - private DartStatement parseErrorStatement() {
|
| - StringBuilder buf = new StringBuilder();
|
| - boolean done = false;
|
| - int braceCount = 1;
|
| - while (!done) {
|
| - buf.append(getPeekTokenValue(0));
|
| - next();
|
| - switch (peek(0)) {
|
| - case RBRACE:
|
| - if (--braceCount == 0) {
|
| - done = true;
|
| - }
|
| - break;
|
| - case LBRACE:
|
| - braceCount++;
|
| - break;
|
| - case EOS:
|
| - case SEMICOLON:
|
| - done = true;
|
| - break;
|
| - }
|
| - }
|
| - return new DartSyntheticErrorStatement(buf.toString());
|
| - }
|
| -
|
| -
|
| - /**
|
| - * Look for a statement terminator, giving error messages and consuming tokens
|
| - * for error recovery.
|
| - */
|
| - protected void expectStatmentTerminator() {
|
| - Token token = peek(0);
|
| - if (expect(Token.SEMICOLON)) {
|
| - return;
|
| - }
|
| - Set<Token> terminals = collectTerminalAnnotations();
|
| - assert(terminals.contains(Token.SEMICOLON));
|
| -
|
| - if (peek(0) == token) {
|
| - reportErrorWithoutAdvancing(ParserErrorCode.EXPECTED_SEMICOLON);
|
| - } else {
|
| - reportError(position(), ParserErrorCode.EXPECTED_SEMICOLON);
|
| - token = peek(0);
|
| - }
|
| -
|
| - // Consume tokens until we see something that could terminate or start a new statement
|
| - while (token != Token.SEMICOLON) {
|
| - if (looksLikeTopLevelKeyword() || terminals.contains(token)) {
|
| - return;
|
| - }
|
| - token = next();
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Report an error without advancing past the next token.
|
| - *
|
| - * @param errCode the error code to report, which may take a string parameter
|
| - * containing the actual token found
|
| - */
|
| - private void reportErrorWithoutAdvancing(ErrorCode errCode) {
|
| - startLookahead();
|
| - Token actual = peek(0);
|
| - next();
|
| - reportError(position(), errCode, actual);
|
| - rollback();
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * iterationStatement
|
| - * : WHILE '(' expression ')' statement
|
| - * | DO statement WHILE '(' expression ')' ';'
|
| - * | FOR '(' forLoopParts ')' statement
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private DartWhileStatement parseWhileStatement() {
|
| - beginWhileStatement();
|
| - expect(Token.WHILE);
|
| - expect(Token.LPAREN);
|
| - DartExpression condition = parseExpression();
|
| - expectCloseParen();
|
| - int closeParenOffset = ctx.getTokenLocation().getBegin();
|
| - DartStatement body = parseLoopStatement();
|
| - return done(new DartWhileStatement(condition, closeParenOffset, body));
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * iterationStatement
|
| - * : WHILE '(' expression ')' statement
|
| - * | DO statement WHILE '(' expression ')' ';'
|
| - * | FOR '(' forLoopParts ')' statement
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private DartDoWhileStatement parseDoWhileStatement() {
|
| - beginDoStatement();
|
| - expect(Token.DO);
|
| - DartStatement body = parseLoopStatement();
|
| - expect(Token.WHILE);
|
| - expect(Token.LPAREN);
|
| - DartExpression condition = parseExpression();
|
| - expectCloseParen();
|
| - expectStatmentTerminator();
|
| - return done(new DartDoWhileStatement(condition, body));
|
| - }
|
| -
|
| - /**
|
| - * Use this wrapper to parse the body of a loop
|
| - *
|
| - * Sets up flag variables to make sure continue and break are properly
|
| - * marked as errors when in wrong context.
|
| - */
|
| - private DartStatement parseLoopStatement() {
|
| - boolean oldInLoop = inLoopStatement;
|
| - inLoopStatement = true;
|
| - try {
|
| - return parseStatement();
|
| - } finally {
|
| - inLoopStatement = oldInLoop;
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * iterationStatement
|
| - * : WHILE '(' expression ')' statement
|
| - * | DO statement WHILE '(' expression ')' ';'
|
| - * | FOR '(' forLoopParts ')' statement
|
| - * ;
|
| - *
|
| - * forLoopParts
|
| - * : forInitializerStatement expression? ';' expressionList?
|
| - * | constVarOrType? identifier IN expression
|
| - * ;
|
| - *
|
| - * forInitializerStatement
|
| - * : initializedVariableDeclaration ';'
|
| - * | expression? ';'
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private DartStatement parseForStatement() {
|
| - beginForStatement();
|
| - expect(Token.FOR);
|
| - expect(Token.LPAREN);
|
| -
|
| - // Setup
|
| - DartStatement setup = null;
|
| - if (peek(0) != Token.SEMICOLON) {
|
| - // Found a setup expression/statement
|
| - beginForInitialization();
|
| - Modifiers modifiers = Modifiers.NONE;
|
| - if (optional(Token.VAR)) {
|
| - setup = done(new DartVariableStatement(parseInitializedVariableList(), null, modifiers));
|
| - } else {
|
| - if (optional(Token.CONST)) {
|
| - modifiers = modifiers.makeConstant();
|
| - }
|
| - if (optional(Token.FINAL)) {
|
| - modifiers = modifiers.makeFinal();
|
| - }
|
| - DartTypeNode type = (peek(1) == Token.IDENTIFIER || peek(1) == Token.LT || peek(1) == Token.PERIOD)
|
| - ? tryTypeAnnotation() : null;
|
| - if (modifiers.isFinal() || type != null) {
|
| - setup = done(new DartVariableStatement(parseInitializedVariableList(), type, modifiers));
|
| - } else {
|
| - setup = done(new DartExprStmt(parseExpression()));
|
| - }
|
| - }
|
| - }
|
| -
|
| - if (optional(Token.IN)) {
|
| - if (setup instanceof DartVariableStatement) {
|
| - DartVariableStatement variableStatement = (DartVariableStatement) setup;
|
| - List<DartVariable> variables = variableStatement.getVariables();
|
| - if (variables.size() != 1) {
|
| - reportError(variables.get(1), ParserErrorCode.FOR_IN_WITH_MULTIPLE_VARIABLES);
|
| - }
|
| - DartExpression initializer = variables.get(0).getValue();
|
| - if (initializer != null) {
|
| - reportError(initializer, ParserErrorCode.FOR_IN_WITH_VARIABLE_INITIALIZER);
|
| - }
|
| - } else {
|
| - DartExpression expression = ((DartExprStmt) setup).getExpression();
|
| - if (!(expression instanceof DartIdentifier)) {
|
| - reportError(setup, ParserErrorCode.FOR_IN_WITH_COMPLEX_VARIABLE);
|
| - }
|
| - }
|
| -
|
| - DartExpression iterable = parseExpression();
|
| - expectCloseParen();
|
| - int closeParenOffset = ctx.getTokenLocation().getBegin();
|
| -
|
| - DartStatement body = parseLoopStatement();
|
| - return done(new DartForInStatement(setup, iterable, closeParenOffset, body));
|
| -
|
| - } else if (optional(Token.SEMICOLON)) {
|
| -
|
| - // Condition
|
| - DartExpression condition = null;
|
| - if (peek(0) != Token.SEMICOLON) {
|
| - condition = parseExpression();
|
| - }
|
| - expect(Token.SEMICOLON);
|
| -
|
| - // Next
|
| - DartExpression next = null;
|
| - if (peek(0) != Token.RPAREN) {
|
| - next = parseExpressionList();
|
| - }
|
| - expectCloseParen();
|
| - int closeParenOffset = ctx.getTokenLocation().getBegin();
|
| -
|
| - DartStatement body = parseLoopStatement();
|
| - return done(new DartForStatement(setup, condition, next, closeParenOffset, body));
|
| - } else {
|
| - reportUnexpectedToken(position(), null, peek(0));
|
| - return done(parseErrorStatement());
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * selectionStatement
|
| - * : IF '(' expression ')' statement ((ELSE)=> ELSE statement)?
|
| - * | SWITCH '(' expression ')' '{' switchCase* defaultCase? '}'
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private DartIfStatement parseIfStatement() {
|
| - beginIfStatement();
|
| - expect(Token.IF);
|
| - expect(Token.LPAREN);
|
| - DartExpression condition = parseExpression();
|
| - expectCloseParen();
|
| - int closeParenOffset = ctx.getTokenLocation().getBegin();
|
| - DartStatement yes = parseStatement();
|
| - DartStatement no = null;
|
| - int elseTokenOffset = 0;
|
| - if (optional(Token.ELSE)) {
|
| - elseTokenOffset = ctx.getTokenLocation().getBegin();
|
| - no = parseStatement();
|
| - }
|
| - return done(new DartIfStatement(condition, closeParenOffset, yes, elseTokenOffset, no));
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * caseStatements
|
| - * : normalCompletingStatement* abruptCompletingStatement
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private List<DartStatement> parseCaseStatements() {
|
| - List<DartStatement> statements = new ArrayList<DartStatement>();
|
| - DartStatement statement = null;
|
| - boolean endOfCaseFound = false;
|
| - boolean warnedUnreachable = false;
|
| - while (true) {
|
| - switch (peek(0)) {
|
| - case CLASS:
|
| - // exit loop to report error condition
|
| - case CASE:
|
| - case DEFAULT:
|
| - case RBRACE:
|
| - case EOS:
|
| - return statements;
|
| - case IDENTIFIER:
|
| - // Handle consecutively labeled case statements
|
| - if (isCaseOrDefault()) {
|
| - return statements;
|
| - }
|
| - default:
|
| - boolean oldInCaseStatement = inCaseStatement;
|
| - inCaseStatement = true;
|
| - try {
|
| - if (endOfCaseFound && !warnedUnreachable) {
|
| - reportErrorWithoutAdvancing(ParserErrorCode.UNREACHABLE_CODE_IN_CASE);
|
| - warnedUnreachable = true;
|
| - }
|
| - statement = parseStatement();
|
| - } finally {
|
| - inCaseStatement = oldInCaseStatement;
|
| - }
|
| - if (statement == null) {
|
| - return statements;
|
| - }
|
| -
|
| - // Don't add unreachable code to the list of statements.
|
| - if (!endOfCaseFound) {
|
| - statements.add(statement);
|
| - if (statement.isAbruptCompletingStatement()) {
|
| - endOfCaseFound = true;
|
| - }
|
| - }
|
| - }
|
| - }
|
| - }
|
| -
|
| - private boolean isCaseOrDefault() {
|
| - int index = 0;
|
| - while (peek(index) == Token.IDENTIFIER && peek(index + 1) == Token.COLON) {
|
| - index += 2;
|
| - }
|
| - Token next = peek(index);
|
| - return next == Token.CASE || next == Token.DEFAULT;
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * switchCase
|
| - * : label? (CASE expression ':')+ caseStatements
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private DartSwitchMember parseCaseMember(List<DartLabel> labels) {
|
| - // The begin() associated with the done() in this method is in the method
|
| - // parseSwitchStatement(), called by beginSwitchMember().
|
| - expect(Token.CASE);
|
| - DartExpression caseExpr = parseExpression();
|
| - expect(Token.COLON);
|
| - return done(new DartCase(caseExpr, labels, parseCaseStatements()));
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * defaultCase
|
| - * : label? (CASE expression ':')* DEFAULT ':' caseStatements
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private DartSwitchMember parseDefaultMember(List<DartLabel> labels) {
|
| - // The begin() associated with the done() in this method is in the method
|
| - // parseSwitchStatement(), called by beginSwitchMember().
|
| - expect(Token.DEFAULT);
|
| - expect(Token.COLON);
|
| - return done(new DartDefault(labels, parseCaseStatements()));
|
| - }
|
| -
|
| -
|
| - /**
|
| - * <pre>
|
| - * selectionStatement
|
| - * : IF '(' expression ')' statement ((ELSE)=> ELSE statement)?
|
| - * | SWITCH '(' expression ')' '{' switchCase* defaultCase? '}'
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private DartStatement parseSwitchStatement() {
|
| - beginSwitchStatement();
|
| - expect(Token.SWITCH);
|
| -
|
| - expect(Token.LPAREN);
|
| - DartExpression expr = parseExpression();
|
| - expectCloseParen();
|
| -
|
| - List<DartSwitchMember> members = new ArrayList<DartSwitchMember>();
|
| - if (peek(0) != Token.LBRACE) {
|
| - reportUnexpectedToken(position(), Token.LBRACE, peek(0));
|
| - return done(new DartSwitchStatement(expr, members));
|
| - }
|
| - boolean foundOpenBrace = expect(Token.LBRACE);
|
| -
|
| - boolean done = optional(Token.RBRACE);
|
| - while (!done) {
|
| - List<DartLabel> labels = new ArrayList<DartLabel>();
|
| - beginSwitchMember(); // switch member
|
| - while (peek(0) == Token.IDENTIFIER && peek(1) == Token.COLON) {
|
| - beginLabel();
|
| - DartIdentifier identifier = parseIdentifier();
|
| - expect(Token.COLON);
|
| - labels.add(done(new DartLabel(identifier, null)));
|
| - if (peek(0) == Token.RBRACE) {
|
| - reportError(position(), ParserErrorCode.LABEL_NOT_FOLLOWED_BY_CASE_OR_DEFAULT);
|
| - expectCloseBrace(foundOpenBrace);
|
| - return done(new DartSwitchStatement(expr, members));
|
| - }
|
| - }
|
| - if (peek(0) == Token.CASE) {
|
| - members.add(parseCaseMember(labels));
|
| - } else if (optional(Token.RBRACE)) {
|
| - if (!labels.isEmpty()) {
|
| - reportError(position(), ParserErrorCode.EXPECTED_CASE_OR_DEFAULT);
|
| - }
|
| - done = true;
|
| - done(null);
|
| - } else {
|
| - if (peek(0) == Token.DEFAULT) {
|
| - members.add(parseDefaultMember(labels));
|
| - }
|
| - expectCloseBrace(foundOpenBrace);
|
| - done = true; // Ensure termination.
|
| - }
|
| - }
|
| - return done(new DartSwitchStatement(expr, members));
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * catchParameter
|
| - * : FINAL type? identifier
|
| - * | VAR identifier
|
| - * | type identifier
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private DartParameter parseCatchParameter() {
|
| - beginCatchParameter();
|
| - List<DartAnnotation> metadata = parseMetadata();
|
| - DartTypeNode type = null;
|
| - Modifiers modifiers = Modifiers.NONE;
|
| - boolean isDeclared = false;
|
| - if (optional(Token.VAR)) {
|
| - isDeclared = true;
|
| - } else {
|
| - if (optional(Token.FINAL)) {
|
| - modifiers = modifiers.makeFinal();
|
| - isDeclared = true;
|
| - }
|
| - if (peek(1) != Token.COMMA && peek(1) != Token.RPAREN) {
|
| - type = parseTypeAnnotation();
|
| - isDeclared = true;
|
| - }
|
| - }
|
| - DartIdentifier name = parseIdentifier();
|
| - if (!isDeclared) {
|
| - reportError(name, ParserErrorCode.EXPECTED_VAR_FINAL_OR_TYPE);
|
| - }
|
| - DartParameter parameter = done(new DartParameter(name, type, null, null, modifiers));
|
| - setMetadata(parameter, metadata);
|
| - return parameter;
|
| - }
|
| -
|
| - /**
|
| - * Parse either the old try statement syntax:
|
| - * <pre>
|
| - * tryStatement
|
| - * : TRY block (catchPart+ finallyPart? | finallyPart)
|
| - * ;
|
| - *
|
| - * catchPart
|
| - * : CATCH '(' declaredIdentifier (',' declaredIdentifier)? ')' block
|
| - * ;
|
| - *
|
| - * finallyPart
|
| - * : FINALLY block
|
| - * ;
|
| - * </pre>
|
| - * or the new syntax:
|
| - * <pre>
|
| - * tryStatement
|
| - * : TRY block (onPart+ finallyPart? | finallyPart)
|
| - * ;
|
| - *
|
| - * onPart
|
| - * : catchPart block
|
| - * | ON qualified catchPart? block
|
| - *
|
| - * catchPart
|
| - * : CATCH '(' identifier (',' identifier)? ')'
|
| - * ;
|
| - *
|
| - * finallyPart
|
| - * : FINALLY block
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private DartTryStatement parseTryStatement() {
|
| - beginTryStatement();
|
| - // Try.
|
| - expect(Token.TRY);
|
| - // TODO(zundel): It would be nice here to setup 'ON', 'CATCH' and 'FINALLY' as tokens for recovery
|
| - DartBlock tryBlock = parseBlock();
|
| -
|
| - List<DartCatchBlock> catches = new ArrayList<DartCatchBlock>();
|
| - while (peekPseudoKeyword(0, ON_KEYWORD) || match(Token.CATCH)) {
|
| - // TODO(zundel): It would be nice here to setup 'FINALLY' as token for recovery
|
| - if (peekPseudoKeyword(0, ON_KEYWORD)) {
|
| - beginCatchClause();
|
| - next();
|
| - int onTokenOffset = position();
|
| - DartTypeNode exceptionType = parseTypeAnnotation();
|
| - DartParameter exception = null;
|
| - DartParameter stackTrace = null;
|
| - if (optional(Token.CATCH)) {
|
| - expect(Token.LPAREN);
|
| - beginCatchParameter();
|
| - List<DartAnnotation> metadata = parseMetadata();
|
| - DartIdentifier exceptionName = parseIdentifier();
|
| - exception = done(new DartParameter(exceptionName, exceptionType, null, null, Modifiers.NONE));
|
| - setMetadata(exception, metadata);
|
| - if (optional(Token.COMMA)) {
|
| - beginCatchParameter();
|
| - DartIdentifier stackName = parseIdentifier();
|
| - stackTrace = done(new DartParameter(stackName, null, null, null, Modifiers.NONE));
|
| - }
|
| - expectCloseParen();
|
| - } else {
|
| - // Create a dummy identifier that the user cannot reliably reference.
|
| - beginCatchParameter();
|
| - List<DartAnnotation> metadata = parseMetadata();
|
| - beginIdentifier();
|
| - DartIdentifier exceptionName = done(new DartIdentifier("e" + Long.toHexString(System.currentTimeMillis())));
|
| - exception = done(new DartParameter(exceptionName, exceptionType, null, null, Modifiers.NONE));
|
| - setMetadata(exception, metadata);
|
| - }
|
| - DartBlock block = parseBlock();
|
| - catches.add(done(new DartCatchBlock(block, onTokenOffset, exception, stackTrace)));
|
| - } else {
|
| - beginCatchClause();
|
| - next();
|
| - expect(Token.LPAREN);
|
| - DartParameter exception;
|
| - if (match(Token.IDENTIFIER) && (peek(1) == Token.COMMA || peek(1) == Token.RPAREN)) {
|
| - beginCatchParameter();
|
| - List<DartAnnotation> metadata = parseMetadata();
|
| - DartIdentifier exceptionName = parseIdentifier();
|
| - exception = done(new DartParameter(exceptionName, null , null, null, Modifiers.NONE));
|
| - setMetadata(exception, metadata);
|
| - } else {
|
| - // Old-style parameter
|
| - reportError(position(), ParserErrorCode.DEPRECATED_CATCH);
|
| - exception = parseCatchParameter();
|
| - }
|
| - DartParameter stackTrace = null;
|
| - if (optional(Token.COMMA)) {
|
| - if (match(Token.IDENTIFIER) && peek(1) == Token.RPAREN) {
|
| - beginCatchParameter();
|
| - List<DartAnnotation> metadata = parseMetadata();
|
| - DartIdentifier stackName = parseIdentifier();
|
| - stackTrace = done(new DartParameter(stackName, null, null, null, Modifiers.NONE));
|
| - setMetadata(stackTrace, metadata);
|
| - } else {
|
| - // Old-style parameter
|
| - reportError(position(), ParserErrorCode.DEPRECATED_CATCH);
|
| - stackTrace = parseCatchParameter();
|
| - }
|
| - }
|
| - expectCloseParen();
|
| - DartBlock block = parseBlock();
|
| - catches.add(done(new DartCatchBlock(block, -1, exception, stackTrace)));
|
| - }
|
| - }
|
| -
|
| - // Finally.
|
| - DartBlock finallyBlock = null;
|
| - if (optional(Token.FINALLY)) {
|
| - finallyBlock = parseBlock();
|
| - }
|
| -
|
| - if ( catches.size() == 0 && finallyBlock == null) {
|
| - reportError(new DartCompilationError(tryBlock.getSourceInfo().getSource(), new Location(position()),
|
| - ParserErrorCode.CATCH_OR_FINALLY_EXPECTED));
|
| - }
|
| -
|
| - return done(new DartTryStatement(tryBlock, catches, finallyBlock));
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * unaryExpression
|
| - * : postfixExpression
|
| - * | prefixOperator unaryExpression
|
| - * | incrementOperator assignableExpression
|
| - * ;
|
| - *
|
| - * @return an expression or null if noFail is true and the next tokens could not be parsed as an
|
| - * expression, leaving the state unchanged.
|
| - * </pre>
|
| - */
|
| - private DartExpression parseUnaryExpression() {
|
| - // There is no unary plus operator in Dart.
|
| - // However, we allow a leading plus in decimal numeric literals.
|
| - if (optional(Token.ADD)) {
|
| - if (peek(0) != Token.INTEGER_LITERAL && peek(0) != Token.DOUBLE_LITERAL) {
|
| - reportError(position(), ParserErrorCode.NO_UNARY_PLUS_OPERATOR);
|
| - } else if (position() + 1 != peekTokenLocation(0).getBegin()) {
|
| - reportError(position(), ParserErrorCode.NO_SPACE_AFTER_PLUS);
|
| - }
|
| - }
|
| - // Check for unary minus operator.
|
| - Token token = peek(0);
|
| - if (token.isUnaryOperator() || token == Token.SUB) {
|
| - if (token == Token.DEC && peek(1) == Token.SUPER) {
|
| - beginUnaryExpression();
|
| - beginUnaryExpression();
|
| - consume(token);
|
| - int tokenOffset = ctx.getTokenLocation().getBegin();
|
| - DartExpression unary = parseUnaryExpression();
|
| - DartUnaryExpression unary2 = new DartUnaryExpression(Token.SUB, tokenOffset, unary, true);
|
| - return done(new DartUnaryExpression(Token.SUB, tokenOffset, done(unary2), true));
|
| - } else {
|
| - beginUnaryExpression();
|
| - consume(token);
|
| - int tokenOffset = ctx.getTokenLocation().getBegin();
|
| - DartExpression unary = parseUnaryExpression();
|
| - if (token.isCountOperator()) {
|
| - ensureAssignable(unary);
|
| - }
|
| - return done(new DartUnaryExpression(token, tokenOffset, unary, true));
|
| - }
|
| - } else {
|
| - return parsePostfixExpression();
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * type
|
| - * : qualified typeArguments?
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private DartTypeNode parseTypeAnnotation() {
|
| - beginTypeAnnotation();
|
| - return done(new DartTypeNode(parseQualified(false), parseTypeArgumentsOpt()));
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * type
|
| - * : qualified typeArguments? ('.' identifier)?
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private DartTypeNode parseTypeAnnotationPossiblyFollowedByName() {
|
| - beginTypeAnnotation();
|
| - boolean canBeFollowedByPeriod = true;
|
| - if (peek(Token.IDENTIFIER, Token.LT) || peek(Token.IDENTIFIER, Token.PERIOD, Token.IDENTIFIER, Token.LT)) {
|
| - canBeFollowedByPeriod = false;
|
| - }
|
| - return done(new DartTypeNode(parseQualified(canBeFollowedByPeriod), parseTypeArgumentsOpt()));
|
| - }
|
| -
|
| - private boolean peek(Token... tokens) {
|
| - int index = 0;
|
| - for (Token token : tokens) {
|
| - if (peek(index++) != token) {
|
| - return false;
|
| - }
|
| - }
|
| - return true;
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * typeArguments
|
| - * : '<' typeList '>'
|
| - * ;
|
| - *
|
| - * typeList
|
| - * : type (',' type)*
|
| - * ;
|
| - * </pre>
|
| - */
|
| - @Terminals(tokens={Token.GT, Token.COMMA})
|
| - private List<DartTypeNode> parseTypeArguments() {
|
| - consume(Token.LT);
|
| - List<DartTypeNode> arguments = new ArrayList<DartTypeNode>();
|
| - do {
|
| - arguments.add(parseTypeAnnotation());
|
| - } while (optional(Token.COMMA));
|
| - if (!tryParameterizedTypeEnd()) {
|
| - expect(Token.GT);
|
| - }
|
| - return arguments;
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * typeArguments?
|
| - * </pre>
|
| - */
|
| - private List<DartTypeNode> parseTypeArgumentsOpt() {
|
| - return (peek(0) == Token.LT)
|
| - ? parseTypeArguments()
|
| - : Collections.<DartTypeNode>emptyList();
|
| - }
|
| -
|
| - /**
|
| - * <pre>
|
| - * qualified
|
| - * : identifier ('.' identifier)?
|
| - * ;
|
| - * </pre>
|
| - */
|
| - private DartExpression parseQualified(boolean canBeFollowedByPeriod) {
|
| - beginQualifiedIdentifier();
|
| - DartIdentifier identifier = parseIdentifier();
|
| - if (!prefixes.contains(identifier.getName())) {
|
| - if (canBeFollowedByPeriod && !(peek(0) == Token.PERIOD && peek(1) == Token.IDENTIFIER && peek(2) == Token.PERIOD)) {
|
| - return done(identifier);
|
| - }
|
| - }
|
| - DartExpression qualified = identifier;
|
| - if (optional(Token.PERIOD)) {
|
| - // The previous identifier was a prefix.
|
| - qualified = new DartPropertyAccess(qualified, parseIdentifier());
|
| - }
|
| - return done(qualified);
|
| - }
|
| -
|
| - private boolean tryParameterizedTypeEnd() {
|
| - switch (peek(0)) {
|
| - case GT:
|
| - consume(Token.GT);
|
| - return true;
|
| - case SAR:
|
| - setPeek(0, Token.GT);
|
| - return true;
|
| - default:
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - private DartTypeNode tryTypeAnnotation() {
|
| - if (peek(0) != Token.IDENTIFIER) {
|
| - return null;
|
| - }
|
| - List<DartTypeNode> typeArguments = new ArrayList<DartTypeNode>();
|
| - beginTypeAnnotation(); // to allow roll-back in case we're not at a type
|
| -
|
| - DartNode qualified = parseQualified(false);
|
| -
|
| - if (optional(Token.LT)) {
|
| - if (peek(0) != Token.IDENTIFIER) {
|
| - rollback();
|
| - return null;
|
| - }
|
| - beginTypeArguments();
|
| - DartNode qualified2 = parseQualified(false);
|
| - DartTypeNode argument;
|
| - switch (peek(0)) {
|
| - case LT:
|
| - // qualified < qualified2 <
|
| - argument = done(new DartTypeNode(qualified2, parseTypeArguments()));
|
| - break;
|
| -
|
| - case GT:
|
| - case SAR:
|
| - // qualified < qualified2 >
|
| - case COMMA:
|
| - // qualified < qualified2 ,
|
| - argument = done(new DartTypeNode(qualified2, Collections.<DartTypeNode>emptyList()));
|
| - break;
|
| -
|
| - default:
|
| - done(null);
|
| - rollback();
|
| - return null;
|
| - }
|
| - typeArguments.add(argument);
|
| -
|
| - while (optional(Token.COMMA)) {
|
| - typeArguments.add(parseTypeAnnotation());
|
| - }
|
| - if (!tryParameterizedTypeEnd()) {
|
| - expect(Token.GT);
|
| - }
|
| - }
|
| -
|
| - return done(new DartTypeNode(qualified, typeArguments));
|
| - }
|
| -
|
| - private DartIdentifier parseIdentifier() {
|
| - beginIdentifier();
|
| - if (looksLikeTopLevelKeyword()) {
|
| - reportErrorWithoutAdvancing(ParserErrorCode.EXPECTED_IDENTIFIER);
|
| - return done(new DartSyntheticErrorIdentifier());
|
| - }
|
| - DartIdentifier identifier;
|
| - if (expect(Token.IDENTIFIER) && ctx.getTokenString() != null) {
|
| - identifier = new DartIdentifier(new String(ctx.getTokenString()));
|
| - } else {
|
| - identifier = new DartSyntheticErrorIdentifier();
|
| - }
|
| - return done(identifier);
|
| - }
|
| -
|
| - public DartExpression parseEntryPoint() {
|
| - beginEntryPoint();
|
| - DartExpression entry = parseIdentifier();
|
| - while (!EOS()) {
|
| - expect(Token.PERIOD);
|
| - entry = doneWithoutConsuming(new DartPropertyAccess(entry, parseIdentifier()));
|
| - }
|
| - return done(entry);
|
| - }
|
| -
|
| - private void ensureAssignable(DartExpression expression) {
|
| - if (expression != null && !expression.isAssignable()) {
|
| - reportError(position(), ParserErrorCode.ILLEGAL_ASSIGNMENT_TO_NON_ASSIGNABLE);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Increment the number of errors encountered while parsing this compilation unit. Returns whether
|
| - * the current error should be reported.
|
| - *
|
| - * @return whether the current error should be reported
|
| - */
|
| - private boolean incErrorCount(ErrorCode errorCode) {
|
| - // count only errors, but not warnings (such as "abstract")
|
| - if (errorCode.getErrorSeverity() == ErrorSeverity.ERROR) {
|
| - errorCount++;
|
| - }
|
| -
|
| - if (errorCount >= MAX_DEFAULT_ERRORS) {
|
| - if (errorCount == MAX_DEFAULT_ERRORS) {
|
| - // Create a 'too many errors' error.
|
| - DartCompilationError dartError = new DartCompilationError(ctx.getSource(),
|
| - ctx.getTokenLocation(), ParserErrorCode.NO_SOUP_FOR_YOU);
|
| - ctx.error(dartError);
|
| - }
|
| -
|
| - // Consume the rest of the input stream. Throwing an exception - as suggested elsewhere in
|
| - // this file - is not ideal.
|
| - Token next = next();
|
| -
|
| - while (next != null && next != Token.EOS) {
|
| - next = next();
|
| - }
|
| - }
|
| -
|
| - return errorCount < MAX_DEFAULT_ERRORS;
|
| - }
|
| -
|
| - private void reportDeprecatedError(int position, ErrorCode errorCode) {
|
| - // TODO(scheglov) remove after http://code.google.com/p/dart/issues/detail?id=6508
|
| - if (
|
| - true
|
| - && !Elements.isCoreLibrarySource(source)
|
| - && !Elements.isLibrarySource(source, "/isolate/isolate.dart")
|
| - && !Elements.isLibrarySource(source, "/json/json.dart")
|
| - && !Elements.isLibrarySource(source, "/math/math.dart")
|
| - && !Elements.isLibrarySource(source, "/io/io.dart")
|
| - && !Elements.isLibrarySource(source, "/crypto/crypto.dart")
|
| - && !Elements.isLibrarySource(source, "/uri/uri.dart")
|
| - && !Elements.isLibrarySource(source, "/utf/utf.dart")
|
| - && !Elements.isLibrarySource(source, "/typed_data/typed_data.dart")
|
| - ) {
|
| - super.reportError(position, errorCode);
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - protected void reportError(int position, ErrorCode errorCode, Object... arguments) {
|
| - // TODO(devoncarew): we're not correctly identifying dart:html as a core library
|
| - if (incErrorCount(errorCode)) {
|
| - super.reportError(position, errorCode, arguments);
|
| - }
|
| - }
|
| -
|
| - @Override
|
| - protected void reportErrorAtPosition(int startPosition, int endPosition,
|
| - ErrorCode errorCode, Object... arguments) {
|
| - if (incErrorCount(errorCode)) {
|
| - super.reportErrorAtPosition(startPosition, endPosition, errorCode, arguments);
|
| - }
|
| - }
|
| -
|
| - private void reportError(DartCompilationError dartError) {
|
| - if (incErrorCount(dartError.getErrorCode())) {
|
| - ctx.error(dartError);
|
| - errorHistory.add(dartError.hashCode());
|
| - }
|
| - }
|
| -
|
| - private void reportError(DartNode node, ErrorCode errorCode, Object... arguments) {
|
| - if (node != null) {
|
| - reportError(new DartCompilationError(node, errorCode, arguments));
|
| - }
|
| - }
|
| -
|
| - private boolean currentlyParsingToplevel() {
|
| - return !(isParsingInterface || isTopLevelAbstract || isParsingClass);
|
| - }
|
| -
|
| - /**
|
| - * @return <code>true</code> if current token is built-in identifier which can have special
|
| - * meaning. For example if it is used as import prefix, this is not special meaning, this is just
|
| - * normal identifier.
|
| - */
|
| - private boolean isBuiltInSpecial() {
|
| - Token nextToken = peek(1);
|
| - if (nextToken == Token.LT) {
|
| - return peek(2) != Token.IDENTIFIER;
|
| - }
|
| - return nextToken != Token.PERIOD && nextToken != Token.LPAREN;
|
| - }
|
| -}
|
|
|