| Index: compiler/java/com/google/dart/compiler/parser/AbstractParser.java
|
| diff --git a/compiler/java/com/google/dart/compiler/parser/AbstractParser.java b/compiler/java/com/google/dart/compiler/parser/AbstractParser.java
|
| index 759b19bb5b79b1fcfc2d2b2418a12eddedac43bb..231c5b399d714c92ce6b1fbe5f32d8779295745c 100644
|
| --- a/compiler/java/com/google/dart/compiler/parser/AbstractParser.java
|
| +++ b/compiler/java/com/google/dart/compiler/parser/AbstractParser.java
|
| @@ -1,13 +1,21 @@
|
| -// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
| +// 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.collect.Lists;
|
| +import com.google.common.collect.Maps;
|
| +import com.google.common.collect.Sets;
|
| import com.google.dart.compiler.DartCompilationError;
|
| import com.google.dart.compiler.ErrorCode;
|
| import com.google.dart.compiler.parser.DartScanner.Location;
|
|
|
| +import java.lang.annotation.Annotation;
|
| +import java.util.List;
|
| +import java.util.Map;
|
| +import java.util.Set;
|
| +
|
| /**
|
| * Abstract base class for sharing common utility methods between implementation
|
| * classes, like {@link DartParser}.
|
| @@ -25,19 +33,100 @@ abstract class AbstractParser {
|
| return match(Token.EOS) || match(Token.ILLEGAL);
|
| }
|
|
|
| + private static class TerminalAnnotationsCache {
|
| + private static Map<String, Class<?>> classes;
|
| + private static Map<String, List<Token>> methods;
|
| +
|
| + private static void init(StackTraceElement[] stackTrace) {
|
| + if (classes == null) {
|
| + classes = Maps.newHashMap();
|
| + methods = Maps.newHashMap();
|
| + }
|
| +
|
| + for (StackTraceElement frame : stackTrace) {
|
| + Class<?> thisClass = classes.get(frame.getClassName());
|
| + if (thisClass == null) {
|
| + try {
|
| + thisClass = Class.forName(frame.getClassName());
|
| +
|
| + for (java.lang.reflect.Method method : thisClass
|
| + .getDeclaredMethods()) {
|
| + List<Token> tokens = methods.get(method.getName());
|
| + if (tokens == null) {
|
| + tokens = Lists.newArrayList();
|
| + methods.put(thisClass.getName() + "." + method.getName(), tokens);
|
| + }
|
| + // look for annotations
|
| + Terminals terminalsAnnotation = (Terminals) method
|
| + .getAnnotation(Terminals.class);
|
| + if (terminalsAnnotation != null) {
|
| + for (Token token : terminalsAnnotation.tokens()) {
|
| + tokens.add(token);
|
| + }
|
| + }
|
| + }
|
| + } catch (ClassNotFoundException e) {
|
| + // ignored
|
| + }
|
| + classes.put(frame.getClassName(), null);
|
| + }
|
| + }
|
| + }
|
| +
|
| + public static Set<Token> terminalsForStack(StackTraceElement[] stackTrace) {
|
| + Set<Token> results = Sets.newHashSet();
|
| + for (StackTraceElement frame: stackTrace) {
|
| + List<Token> found = methods.get(frame.getClassName() + "." + frame.getMethodName());
|
| + if (found != null) {
|
| + results.addAll(found);
|
| + }
|
| + }
|
| + return results;
|
| + }
|
| + }
|
| +
|
| +
|
| + /**
|
| + * Uses reflection to walk up the stack and look for @Terminals method
|
| + * annotations. It gathers up the tokens in these annotations and returns them
|
| + * to the caller. This is intended for use in parser recovery, so that we
|
| + * don't accidentally consume a token that could be used to complete a
|
| + * non-terminal higher up in the stack.
|
| + */
|
| + protected Set<Token> collectTerminalAnnotations() {
|
| + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
| + // Get methods for every class and associated Terminals annotations & stick them in a hash
|
| + TerminalAnnotationsCache.init(stackTrace);
|
| + // Create the set of terminals to return
|
| + return TerminalAnnotationsCache.terminalsForStack(stackTrace);
|
| + }
|
| +
|
| protected boolean expect(Token expectedToken) {
|
| if (!optional(expectedToken)) {
|
| +
|
| /*
|
| * Save the current token, then advance to make sure that we have the
|
| * right position.
|
| */
|
| Token actualToken = peek(0);
|
| +
|
| + Set<Token> possibleTerminals = collectTerminalAnnotations();
|
| +
|
| + ctx.begin();
|
| ctx.advance();
|
| reportUnexpectedToken(position(), expectedToken, actualToken);
|
| + // Don't consume tokens someone else could use to cleanly terminate the
|
| + // statement.
|
| + if (possibleTerminals.contains(actualToken)) {
|
| + ctx.rollback();
|
| + return false;
|
| + }
|
| + ctx.done(null);
|
| +
|
| // Recover from the middle of string interpolation
|
| - if (actualToken.equals(Token.STRING_EMBED_EXP_START) ||
|
| - actualToken.equals(Token.STRING_EMBED_EXP_END)) {
|
| - while(!EOS()) {
|
| + if (actualToken.equals(Token.STRING_EMBED_EXP_START)
|
| + || actualToken.equals(Token.STRING_EMBED_EXP_END)) {
|
| + while (!EOS()) {
|
| Token nextToken = next();
|
| if (nextToken.equals(Token.STRING_LAST_SEGMENT)) {
|
| break;
|
| @@ -45,6 +134,7 @@ abstract class AbstractParser {
|
| next();
|
| }
|
| }
|
| +
|
| return false;
|
| }
|
| return true;
|
| @@ -86,34 +176,36 @@ abstract class AbstractParser {
|
| }
|
|
|
| protected boolean peekPseudoKeyword(int n, String keyword) {
|
| - return (peek(n) == Token.IDENTIFIER) && keyword.equals(getPeekTokenValue(n));
|
| + return (peek(n) == Token.IDENTIFIER)
|
| + && keyword.equals(getPeekTokenValue(n));
|
| }
|
|
|
| protected DartScanner.Position position() {
|
| DartScanner.Location tokenLocation = ctx.getTokenLocation();
|
| - return tokenLocation != null ? tokenLocation.getBegin() : new DartScanner.Position(0, 1, 1);
|
| + return tokenLocation != null ? tokenLocation.getBegin()
|
| + : new DartScanner.Position(0, 1, 1);
|
| }
|
|
|
| /**
|
| - * Report a syntax error, unless an error has already been reported at the given or a later
|
| - * position.
|
| + * Report a syntax error, unless an error has already been reported at the
|
| + * given or a later position.
|
| */
|
| - protected void reportError(DartScanner.Position position, ErrorCode errorCode,
|
| - Object... arguments) {
|
| + protected void reportError(DartScanner.Position position,
|
| + ErrorCode errorCode, Object... arguments) {
|
| DartScanner.Location location = ctx.getTokenLocation();
|
| if (location.getBegin().getPos() <= lastErrorPosition) {
|
| return;
|
| }
|
| - DartCompilationError dartError = new DartCompilationError(ctx.getSource(), location, errorCode,
|
| - arguments);
|
| + DartCompilationError dartError = new DartCompilationError(ctx.getSource(),
|
| + location, errorCode, arguments);
|
| lastErrorPosition = position.getPos();
|
| ctx.error(dartError);
|
| }
|
| -
|
| +
|
| /**
|
| - * Even though you pass a 'Position' to {@link #reportError} above, it only uses that to
|
| - * prevent logging more than one error at that position. This method actually uses the passed
|
| - * position to create the error event.
|
| + * Even though you pass a 'Position' to {@link #reportError} above, it only
|
| + * uses that to prevent logging more than one error at that position. This
|
| + * method actually uses the passed position to create the error event.
|
| */
|
| protected void reportErrorAtPosition(DartScanner.Position startPosition,
|
| DartScanner.Position endPosition,
|
| @@ -127,8 +219,8 @@ abstract class AbstractParser {
|
| ctx.error(dartError);
|
| }
|
|
|
| - protected void reportUnexpectedToken(DartScanner.Position position, Token expected,
|
| - Token actual) {
|
| + protected void reportUnexpectedToken(DartScanner.Position position,
|
| + Token expected, Token actual) {
|
| if (expected == Token.EOS) {
|
| reportError(position, ParserErrorCode.EXPECTED_EOS, actual);
|
| } else if (expected == null) {
|
|
|