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; |
- } |
-} |