Index: pkg/dart_parser/lib/src/listener.dart |
diff --git a/pkg/dart_parser/lib/src/listener.dart b/pkg/dart_parser/lib/src/listener.dart |
index 9d656bbcf94dd29adb6d08d793bf9ed2e8dfaf18..63d0188d6f021a7f083afb978300b04a372429f8 100644 |
--- a/pkg/dart_parser/lib/src/listener.dart |
+++ b/pkg/dart_parser/lib/src/listener.dart |
@@ -4,18 +4,9 @@ |
library dart_parser.listener; |
-import 'package:dart_scanner/src/precedence.dart' show |
- EOF_INFO, |
- IDENTIFIER_INFO; |
- |
import 'package:dart_scanner/src/token.dart' show |
- BadInputToken, |
BeginGroupToken, |
- ErrorToken, |
- StringToken, |
- Token, |
- UnmatchedToken, |
- UnterminatedToken; |
+ Token; |
import 'error_kind.dart' show |
ErrorKind; |
@@ -760,216 +751,28 @@ class Listener { |
logEvent("YieldStatement"); |
} |
- // TODO(ahe): Rename to `handleUnexpected`. |
- Token expected(String string, Token token) { |
- if (token is ErrorToken) { |
- reportErrorToken(token); |
- } else { |
- error("expected '$string', but got '${token.value}'", token); |
- } |
- return skipToEof(token); |
- } |
- |
- // TODO(ahe): Move away from this class. |
- Token synthesizeIdentifier(Token token) { |
- Token synthesizedToken = new StringToken.fromString( |
- IDENTIFIER_INFO, '?', token.charOffset); |
- synthesizedToken.next = token.next; |
- return synthesizedToken; |
- } |
- |
- // TODO(ahe): Rename to `handleIdentifierExpected`. |
- Token expectedIdentifier(Token token) { |
- if (token is ErrorToken) { |
- reportErrorToken(token); |
- } else { |
- error("expected identifier, but got '${token.value}'", token); |
- } |
- return skipToEof(token); |
- } |
- |
- // TODO(ahe): Rename to `handleTypeExpected`. |
- Token expectedType(Token token) { |
- if (token is ErrorToken) { |
- reportErrorToken(token); |
- } else { |
- error("expected a type, but got '${token.value}'", token); |
- } |
- return skipToEof(token); |
- } |
- |
- // TODO(ahe): Rename to `handleExpressionExpected`. |
- Token expectedExpression(Token token) { |
- if (token is ErrorToken) { |
- reportErrorToken(token); |
- } else { |
- error("expected an expression, but got '${token.value}'", token); |
- } |
- return skipToEof(token); |
- } |
- |
- // TODO(ahe): Merge with `handleUnexpected` (nee `expected`). |
- Token unexpected(Token token) { |
- if (token is ErrorToken) { |
- reportErrorToken(token); |
- } else { |
- error("unexpected token '${token.value}'", token); |
- } |
- return skipToEof(token); |
- } |
- |
- // TODO(ahe): Rename to `handleBlockToSkipExpected`. |
- Token expectedBlockToSkip(Token token) { |
- if (token is ErrorToken) { |
- reportErrorToken(token); |
- } else { |
- error("expected a block, but got '${token.value}'", token); |
- } |
- return skipToEof(token); |
- } |
- |
- // TODO(ahe): Rename to `handleFunctionBodyExpected`. |
- Token expectedFunctionBody(Token token) { |
- if (token is ErrorToken) { |
- reportErrorToken(token); |
- } else { |
- error("expected a function body, but got '${token.value}'", token); |
- } |
- return skipToEof(token); |
- } |
- |
- // TODO(ahe): Rename to `handleClassBodyExpected`. |
- Token expectedClassBody(Token token) { |
- if (token is ErrorToken) { |
- reportErrorToken(token); |
- } else { |
- error("expected a class body, but got '${token.value}'", token); |
- } |
- return skipToEof(token); |
- } |
- |
- // TODO(ahe): Rename to `handleClassBodyToSkipExpected`. |
- Token expectedClassBodyToSkip(Token token) { |
- if (token is ErrorToken) { |
- reportErrorToken(token); |
- } else { |
- error("expected a class body, but got '${token.value}'", token); |
- } |
- return skipToEof(token); |
- } |
- |
- // TODO(ahe): Rename to `handleDeclarationExpected`. |
- Token expectedDeclaration(Token token) { |
- if (token is ErrorToken) { |
- reportErrorToken(token); |
- } else { |
- error("expected a declaration, but got '${token.value}'", token); |
- } |
- return skipToEof(token); |
- } |
- |
- // TODO(ahe): Rename to `handleUnmatched`. |
- Token unmatched(Token token) { |
- if (token is ErrorToken) { |
- reportErrorToken(token); |
- } else { |
- error("unmatched '${token.value}'", token); |
- } |
- return skipToEof(token); |
- } |
- |
- // TODO(ahe): Move away from this class. |
- Token skipToEof(Token token) { |
- while (!identical(token.info, EOF_INFO)) { |
- token = token.next; |
- } |
- return token; |
- } |
- |
- // TODO(ahe): Merge with `handleError` (nee `reportError`). |
- void error(String message, Token token) { |
- throw new ParserError.fromTokens( |
- token, token, ErrorKind.Unspecified, {'text': message}); |
- } |
- |
- // TODO(ahe): Rename to `handleError`. |
- void reportError(Token token, ErrorKind kind, [Map arguments = const {}]) { |
- if (token is ErrorToken) { |
- reportErrorToken(token); |
- } else { |
- reportErrorHelper(token, kind, arguments); |
- } |
- } |
- |
- // TODO(ahe): Move away from this class. |
- void reportErrorHelper(Token token, ErrorKind kind, Map arguments) { |
- recoverableErrors.add( |
- new ParserError.fromTokens(token, token, kind, arguments)); |
+ /// An unrecoverable error is an error that the parser can't recover from |
+ /// itself, and recovery is left to the listener. If the listener can |
+ /// recover, it should return a non-null continuation token. Error recovery |
+ /// is tightly coupled to the parser implementation, so to recover from an |
+ /// error, one must carefully examine the code in the parser that generates |
+ /// the error. |
+ /// |
+ /// If the listener can't recover, it can throw an exception or return |
+ /// `null`. In the latter case, the parser simply skips to EOF which will |
+ /// often result in additional parser errors as the parser returns from its |
+ /// recursive state. |
+ Token handleUnrecoverableError(Token token, ErrorKind kind, Map arguments) { |
+ throw new ParserError.fromTokens(token, token, kind, arguments); |
} |
- // TODO(ahe): Move away from this class. |
- void reportErrorToken(ErrorToken token) { |
- if (token is BadInputToken) { |
- String hex = token.character.toRadixString(16); |
- if (hex.length < 4) { |
- String padding = "0000".substring(hex.length); |
- hex = "$padding$hex"; |
- } |
- reportErrorHelper( |
- token, ErrorKind.InvalidInputCharacter, {'characterHex': hex}); |
- } else if (token is UnterminatedToken) { |
- ErrorKind kind; |
- var arguments = const {}; |
- switch (token.start) { |
- case '1e': |
- kind = ErrorKind.MissingExponent; |
- break; |
- case '"': |
- case "'": |
- case '"""': |
- case "'''": |
- case 'r"': |
- case "r'": |
- case 'r"""': |
- case "r'''": |
- kind = ErrorKind.UnterminatedString; |
- arguments = {'quote': token.start}; |
- break; |
- case '0x': |
- kind = ErrorKind.ExpectedHexDigit; |
- break; |
- case r'$': |
- kind = ErrorKind.MalformedStringLiteral; |
- break; |
- case '/*': |
- kind = ErrorKind.UnterminatedComment; |
- break; |
- default: |
- kind = ErrorKind.UnterminatedToken; |
- break; |
- } |
- reportErrorHelper(token, kind, arguments); |
- } else if (token is UnmatchedToken) { |
- String begin = token.begin.value; |
- String end = closeBraceFor(begin); |
- reportErrorHelper( |
- token, ErrorKind.UnmatchedToken, {'begin': begin, 'end': end}); |
- } else { |
- error(token.assertionMessage, token); |
- } |
+ /// The parser noticed a syntax error, but was able to recover from it. |
+ void handleRecoverableError(Token token, ErrorKind kind, Map arguments) { |
+ recoverableErrors.add( |
+ new ParserError.fromTokens(token, token, kind, arguments)); |
} |
} |
-String closeBraceFor(String openBrace) { |
- return const { |
- '(': ')', |
- '[': ']', |
- '{': '}', |
- '<': '>', |
- r'${': '}', |
- }[openBrace]; |
-} |
- |
class ParserError { |
/// Character offset from the beginning of file where this error starts. |
final int beginOffset; |