| OLD | NEW |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 package com.google.dart.compiler.parser; | 5 package com.google.dart.compiler.parser; |
| 6 | 6 |
| 7 import com.google.common.collect.Lists; |
| 8 import com.google.common.collect.Maps; |
| 9 import com.google.common.collect.Sets; |
| 7 import com.google.dart.compiler.DartCompilationError; | 10 import com.google.dart.compiler.DartCompilationError; |
| 8 import com.google.dart.compiler.ErrorCode; | 11 import com.google.dart.compiler.ErrorCode; |
| 9 import com.google.dart.compiler.parser.DartScanner.Location; | 12 import com.google.dart.compiler.parser.DartScanner.Location; |
| 10 | 13 |
| 14 import java.lang.annotation.Annotation; |
| 15 import java.util.List; |
| 16 import java.util.Map; |
| 17 import java.util.Set; |
| 18 |
| 11 /** | 19 /** |
| 12 * Abstract base class for sharing common utility methods between implementation | 20 * Abstract base class for sharing common utility methods between implementation |
| 13 * classes, like {@link DartParser}. | 21 * classes, like {@link DartParser}. |
| 14 */ | 22 */ |
| 15 abstract class AbstractParser { | 23 abstract class AbstractParser { |
| 16 | 24 |
| 17 protected final ParserContext ctx; | 25 protected final ParserContext ctx; |
| 18 private int lastErrorPosition = Integer.MIN_VALUE; | 26 private int lastErrorPosition = Integer.MIN_VALUE; |
| 19 | 27 |
| 20 protected AbstractParser(ParserContext ctx) { | 28 protected AbstractParser(ParserContext ctx) { |
| 21 this.ctx = ctx; | 29 this.ctx = ctx; |
| 22 } | 30 } |
| 23 | 31 |
| 24 protected boolean EOS() { | 32 protected boolean EOS() { |
| 25 return match(Token.EOS) || match(Token.ILLEGAL); | 33 return match(Token.EOS) || match(Token.ILLEGAL); |
| 26 } | 34 } |
| 27 | 35 |
| 36 private static class TerminalAnnotationsCache { |
| 37 private static Map<String, Class<?>> classes; |
| 38 private static Map<String, List<Token>> methods; |
| 39 |
| 40 private static void init(StackTraceElement[] stackTrace) { |
| 41 if (classes == null) { |
| 42 classes = Maps.newHashMap(); |
| 43 methods = Maps.newHashMap(); |
| 44 } |
| 45 |
| 46 for (StackTraceElement frame : stackTrace) { |
| 47 Class<?> thisClass = classes.get(frame.getClassName()); |
| 48 if (thisClass == null) { |
| 49 try { |
| 50 thisClass = Class.forName(frame.getClassName()); |
| 51 |
| 52 for (java.lang.reflect.Method method : thisClass |
| 53 .getDeclaredMethods()) { |
| 54 List<Token> tokens = methods.get(method.getName()); |
| 55 if (tokens == null) { |
| 56 tokens = Lists.newArrayList(); |
| 57 methods.put(thisClass.getName() + "." + method.getName(), tokens
); |
| 58 } |
| 59 // look for annotations |
| 60 Terminals terminalsAnnotation = (Terminals) method |
| 61 .getAnnotation(Terminals.class); |
| 62 if (terminalsAnnotation != null) { |
| 63 for (Token token : terminalsAnnotation.tokens()) { |
| 64 tokens.add(token); |
| 65 } |
| 66 } |
| 67 } |
| 68 } catch (ClassNotFoundException e) { |
| 69 // ignored |
| 70 } |
| 71 classes.put(frame.getClassName(), null); |
| 72 } |
| 73 } |
| 74 } |
| 75 |
| 76 public static Set<Token> terminalsForStack(StackTraceElement[] stackTrace) { |
| 77 Set<Token> results = Sets.newHashSet(); |
| 78 for (StackTraceElement frame: stackTrace) { |
| 79 List<Token> found = methods.get(frame.getClassName() + "." + frame.getMe
thodName()); |
| 80 if (found != null) { |
| 81 results.addAll(found); |
| 82 } |
| 83 } |
| 84 return results; |
| 85 } |
| 86 } |
| 87 |
| 88 |
| 89 /** |
| 90 * Uses reflection to walk up the stack and look for @Terminals method |
| 91 * annotations. It gathers up the tokens in these annotations and returns them |
| 92 * to the caller. This is intended for use in parser recovery, so that we |
| 93 * don't accidentally consume a token that could be used to complete a |
| 94 * non-terminal higher up in the stack. |
| 95 */ |
| 96 protected Set<Token> collectTerminalAnnotations() { |
| 97 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); |
| 98 // Get methods for every class and associated Terminals annotations & stick
them in a hash |
| 99 TerminalAnnotationsCache.init(stackTrace); |
| 100 // Create the set of terminals to return |
| 101 return TerminalAnnotationsCache.terminalsForStack(stackTrace); |
| 102 } |
| 103 |
| 28 protected boolean expect(Token expectedToken) { | 104 protected boolean expect(Token expectedToken) { |
| 29 if (!optional(expectedToken)) { | 105 if (!optional(expectedToken)) { |
| 106 |
| 30 /* | 107 /* |
| 31 * Save the current token, then advance to make sure that we have the | 108 * Save the current token, then advance to make sure that we have the |
| 32 * right position. | 109 * right position. |
| 33 */ | 110 */ |
| 34 Token actualToken = peek(0); | 111 Token actualToken = peek(0); |
| 112 |
| 113 Set<Token> possibleTerminals = collectTerminalAnnotations(); |
| 114 |
| 115 ctx.begin(); |
| 35 ctx.advance(); | 116 ctx.advance(); |
| 36 reportUnexpectedToken(position(), expectedToken, actualToken); | 117 reportUnexpectedToken(position(), expectedToken, actualToken); |
| 118 // Don't consume tokens someone else could use to cleanly terminate the |
| 119 // statement. |
| 120 if (possibleTerminals.contains(actualToken)) { |
| 121 ctx.rollback(); |
| 122 return false; |
| 123 } |
| 124 ctx.done(null); |
| 125 |
| 37 // Recover from the middle of string interpolation | 126 // Recover from the middle of string interpolation |
| 38 if (actualToken.equals(Token.STRING_EMBED_EXP_START) || | 127 if (actualToken.equals(Token.STRING_EMBED_EXP_START) |
| 39 actualToken.equals(Token.STRING_EMBED_EXP_END)) { | 128 || actualToken.equals(Token.STRING_EMBED_EXP_END)) { |
| 40 while(!EOS()) { | 129 while (!EOS()) { |
| 41 Token nextToken = next(); | 130 Token nextToken = next(); |
| 42 if (nextToken.equals(Token.STRING_LAST_SEGMENT)) { | 131 if (nextToken.equals(Token.STRING_LAST_SEGMENT)) { |
| 43 break; | 132 break; |
| 44 } | 133 } |
| 45 next(); | 134 next(); |
| 46 } | 135 } |
| 47 } | 136 } |
| 137 |
| 48 return false; | 138 return false; |
| 49 } | 139 } |
| 50 return true; | 140 return true; |
| 51 } | 141 } |
| 52 | 142 |
| 53 protected String getPeekTokenValue(int n) { | 143 protected String getPeekTokenValue(int n) { |
| 54 assert (n >= 0); | 144 assert (n >= 0); |
| 55 String value = ctx.peekTokenString(n); | 145 String value = ctx.peekTokenString(n); |
| 56 return value; | 146 return value; |
| 57 } | 147 } |
| (...skipping 21 matching lines...) Expand all Loading... |
| 79 } | 169 } |
| 80 next(); | 170 next(); |
| 81 return true; | 171 return true; |
| 82 } | 172 } |
| 83 | 173 |
| 84 protected Token peek(int n) { | 174 protected Token peek(int n) { |
| 85 return ctx.peek(n); | 175 return ctx.peek(n); |
| 86 } | 176 } |
| 87 | 177 |
| 88 protected boolean peekPseudoKeyword(int n, String keyword) { | 178 protected boolean peekPseudoKeyword(int n, String keyword) { |
| 89 return (peek(n) == Token.IDENTIFIER) && keyword.equals(getPeekTokenValue(n))
; | 179 return (peek(n) == Token.IDENTIFIER) |
| 180 && keyword.equals(getPeekTokenValue(n)); |
| 90 } | 181 } |
| 91 | 182 |
| 92 protected DartScanner.Position position() { | 183 protected DartScanner.Position position() { |
| 93 DartScanner.Location tokenLocation = ctx.getTokenLocation(); | 184 DartScanner.Location tokenLocation = ctx.getTokenLocation(); |
| 94 return tokenLocation != null ? tokenLocation.getBegin() : new DartScanner.Po
sition(0, 1, 1); | 185 return tokenLocation != null ? tokenLocation.getBegin() |
| 186 : new DartScanner.Position(0, 1, 1); |
| 95 } | 187 } |
| 96 | 188 |
| 97 /** | 189 /** |
| 98 * Report a syntax error, unless an error has already been reported at the giv
en or a later | 190 * Report a syntax error, unless an error has already been reported at the |
| 99 * position. | 191 * given or a later position. |
| 100 */ | 192 */ |
| 101 protected void reportError(DartScanner.Position position, ErrorCode errorCode, | 193 protected void reportError(DartScanner.Position position, |
| 102 Object... arguments) { | 194 ErrorCode errorCode, Object... arguments) { |
| 103 DartScanner.Location location = ctx.getTokenLocation(); | 195 DartScanner.Location location = ctx.getTokenLocation(); |
| 104 if (location.getBegin().getPos() <= lastErrorPosition) { | 196 if (location.getBegin().getPos() <= lastErrorPosition) { |
| 105 return; | 197 return; |
| 106 } | 198 } |
| 107 DartCompilationError dartError = new DartCompilationError(ctx.getSource(), l
ocation, errorCode, | 199 DartCompilationError dartError = new DartCompilationError(ctx.getSource(), |
| 108 arguments); | 200 location, errorCode, arguments); |
| 109 lastErrorPosition = position.getPos(); | 201 lastErrorPosition = position.getPos(); |
| 110 ctx.error(dartError); | 202 ctx.error(dartError); |
| 111 } | 203 } |
| 112 | 204 |
| 113 /** | 205 /** |
| 114 * Even though you pass a 'Position' to {@link #reportError} above, it only us
es that to | 206 * Even though you pass a 'Position' to {@link #reportError} above, it only |
| 115 * prevent logging more than one error at that position. This method actually
uses the passed | 207 * uses that to prevent logging more than one error at that position. This |
| 116 * position to create the error event. | 208 * method actually uses the passed position to create the error event. |
| 117 */ | 209 */ |
| 118 protected void reportErrorAtPosition(DartScanner.Position startPosition, | 210 protected void reportErrorAtPosition(DartScanner.Position startPosition, |
| 119 DartScanner.Position endPosition, | 211 DartScanner.Position endPosition, |
| 120 ErrorCode errorCode, Object... arguments)
{ | 212 ErrorCode errorCode, Object... arguments)
{ |
| 121 DartScanner.Location location = ctx.getTokenLocation(); | 213 DartScanner.Location location = ctx.getTokenLocation(); |
| 122 if (location.getBegin().getPos() <= lastErrorPosition) { | 214 if (location.getBegin().getPos() <= lastErrorPosition) { |
| 123 return; | 215 return; |
| 124 } | 216 } |
| 125 DartCompilationError dartError = new DartCompilationError(ctx.getSource(), | 217 DartCompilationError dartError = new DartCompilationError(ctx.getSource(), |
| 126 new Location(startPosition, endPosition), errorCode, arguments); | 218 new Location(startPosition, endPosition), errorCode, arguments); |
| 127 ctx.error(dartError); | 219 ctx.error(dartError); |
| 128 } | 220 } |
| 129 | 221 |
| 130 protected void reportUnexpectedToken(DartScanner.Position position, Token expe
cted, | 222 protected void reportUnexpectedToken(DartScanner.Position position, |
| 131 Token actual) { | 223 Token expected, Token actual) { |
| 132 if (expected == Token.EOS) { | 224 if (expected == Token.EOS) { |
| 133 reportError(position, ParserErrorCode.EXPECTED_EOS, actual); | 225 reportError(position, ParserErrorCode.EXPECTED_EOS, actual); |
| 134 } else if (expected == null) { | 226 } else if (expected == null) { |
| 135 reportError(position, ParserErrorCode.UNEXPECTED_TOKEN, actual); | 227 reportError(position, ParserErrorCode.UNEXPECTED_TOKEN, actual); |
| 136 } else { | 228 } else { |
| 137 reportError(position, ParserErrorCode.EXPECTED_TOKEN, actual, expected); | 229 reportError(position, ParserErrorCode.EXPECTED_TOKEN, actual, expected); |
| 138 } | 230 } |
| 139 } | 231 } |
| 140 | 232 |
| 141 protected void setPeek(int n, Token token) { | 233 protected void setPeek(int n, Token token) { |
| 142 assert n == 0; // so far, n is always zero | 234 assert n == 0; // so far, n is always zero |
| 143 ctx.replaceNextToken(token); | 235 ctx.replaceNextToken(token); |
| 144 } | 236 } |
| 145 | 237 |
| 146 protected boolean consume(Token token) { | 238 protected boolean consume(Token token) { |
| 147 boolean result = (peek(0) == token); | 239 boolean result = (peek(0) == token); |
| 148 assert (result); | 240 assert (result); |
| 149 next(); | 241 next(); |
| 150 return result; | 242 return result; |
| 151 } | 243 } |
| 152 } | 244 } |
| OLD | NEW |