Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(443)

Unified Diff: pkg/front_end/lib/src/fasta/analyzer/token_utils.dart

Issue 2693403002: Change toAnalyzerTokenStream into a class. (Closed)
Patch Set: Rework Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | pkg/front_end/test/scanner_fasta_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/front_end/lib/src/fasta/analyzer/token_utils.dart
diff --git a/pkg/front_end/lib/src/fasta/analyzer/token_utils.dart b/pkg/front_end/lib/src/fasta/analyzer/token_utils.dart
index 4b468f4d04edc9546839cf8822167659b47c969b..08a6ce70b1d5d08a568712bd5ffc75c8337b8fac 100644
--- a/pkg/front_end/lib/src/fasta/analyzer/token_utils.dart
+++ b/pkg/front_end/lib/src/fasta/analyzer/token_utils.dart
@@ -45,83 +45,192 @@ import 'package:analyzer/dart/ast/token.dart' show
import '../errors.dart' show
internalError;
-/// Converts a stream of Fasta tokens (starting with [token] and continuing to
-/// EOF) to a stream of analyzer tokens.
+/// Class capable of converting a stream of Fasta tokens to a stream of analyzer
+/// tokens.
///
-/// If any error tokens are found in the stream, they are reported using the
-/// [reportError] callback.
-analyzer.Token toAnalyzerTokenStream(
- Token token,
- void reportError(analyzer.ScannerErrorCode errorCode, int offset,
- List<Object> arguments)) {
- var analyzerTokenHead = new analyzer.Token(null, 0);
- analyzerTokenHead.previous = analyzerTokenHead;
- var analyzerTokenTail = analyzerTokenHead;
- // TODO(paulberry,ahe): Fasta includes comments directly in the token
- // stream, rather than pointing to them via a "precedingComment" pointer, as
- // analyzer does. This seems like it will complicate parsing and other
- // operations.
- analyzer.CommentToken currentCommentHead;
- analyzer.CommentToken currentCommentTail;
-
- // Both fasta and analyzer have links from a "BeginToken" to its matching
- // "EndToken" in a group (like parentheses and braces). However, fasta may
- // contain synthetic tokens from error recovery that are not mapped to the
- // analyzer token stream. We use these stacks to create the appropriate links
- // for non-synthetic tokens in the way analyzer expects.
+/// This is a class rather than an ordinary method so that it can be subclassed
+/// in tests.
+///
+/// TODO(paulberry,ahe): Fasta includes comments directly in the token
+/// stream, rather than pointing to them via a "precedingComment" pointer, as
+/// analyzer does. This seems like it will complicate parsing and other
+/// operations.
+class ToAnalyzerTokenStreamConverter {
+ /// Synthetic token pointing to the first token in the analyzer token stream.
+ analyzer.Token _analyzerTokenHead;
+
+ /// The most recently generated analyzer token, or [_analyzerTokenHead] if no
+ /// tokens have been generated yet.
+ analyzer.Token _analyzerTokenTail;
+
+ /// If a sequence of consecutive comment tokens is being processed, the first
+ /// translated analyzer comment token. Otherwise `null`.
+ analyzer.CommentToken _currentCommentHead;
+
+ /// If a sequence of consecutive comment tokens is being processed, the last
+ /// translated analyzer comment token. Otherwise `null`.
+ analyzer.CommentToken _currentCommentTail;
+
+ /// Stack of analyzer "begin" tokens which need to be linked up to
+ /// corresponding "end" tokens once those tokens are translated.
+ ///
+ /// The first element of this list is always a sentinel `null` value so that
+ /// we don't have to check if it is empty.
+ ///
+ /// See additional documentation in [_matchGroups].
+ List<analyzer.BeginToken> _beginTokenStack;
+
+ /// Stack of fasta "end" tokens corresponding to the tokens in
+ /// [_endTokenStack].
+ ///
+ /// The first element of this list is always a sentinel `null` value so that
+ /// we don't have to check if it is empty.
+ ///
+ /// See additional documentation in [_matchGroups].
+ List<Token> _endTokenStack;
+
+ /// Converts a stream of Fasta tokens (starting with [token] and continuing to
+ /// EOF) to a stream of analyzer tokens.
+ analyzer.Token convertTokens(Token token) {
+ _analyzerTokenHead = new analyzer.Token(null, 0);
+ _analyzerTokenHead.previous = _analyzerTokenHead;
+ _analyzerTokenTail = _analyzerTokenHead;
+ _currentCommentHead = null;
+ _currentCommentTail = null;
+ _beginTokenStack = [null];
+ _endTokenStack = <Token>[null];
+
+ while (true) {
ahe 2017/02/15 22:09:20 Very very optional: I'm always looking for infinit
Paul Berry 2017/02/16 02:01:23 Acknowledged.
+ if (token.info.kind == BAD_INPUT_TOKEN) {
+ ErrorToken errorToken = token;
+ _translateErrorToken(errorToken);
+ } else if (token.info.kind == COMMENT_TOKEN) {
+ var translatedToken = translateCommentToken(token);
+ if (_currentCommentHead == null) {
+ _currentCommentHead = _currentCommentTail = translatedToken;
+ } else {
+ _currentCommentTail.setNext(translatedToken);
+ _currentCommentTail = translatedToken;
+ }
+ } else {
+ var translatedToken = translateToken(token, _currentCommentHead);
+ _matchGroups(token, translatedToken);
+ translatedToken.setNext(translatedToken);
+ _currentCommentHead = _currentCommentTail = null;
+ _analyzerTokenTail.setNext(translatedToken);
+ translatedToken.previous = _analyzerTokenTail;
+ _analyzerTokenTail = translatedToken;
+ }
+ if (token.isEof) {
+ return _analyzerTokenHead.next;
+ }
+ token = token.next;
+ }
+ }
- // Note: beginTokenStack and endTokenStack are seeded with a sentinel value
- // so that we don't have to check if they're empty.
- var beginTokenStack = <analyzer.BeginToken>[null];
- var endTokenStack = <Token>[null];
+ /// Handles an error found during [convertTokens].
+ ///
+ /// Intended to be overridden by derived classes; by default, does nothing.
+ void reportError(analyzer.ScannerErrorCode errorCode, int offset,
+ List<Object> arguments) {}
+
+ /// Translates a single fasta comment token to the corresponding analyzer
+ /// token.
+ analyzer.CommentToken translateCommentToken(Token token) {
+ // TODO(paulberry,ahe): It would be nice if the scanner gave us an
+ // easier way to distinguish between the two types of comment.
+ var type = token.value.startsWith('/*')
+ ? TokenType.MULTI_LINE_COMMENT
+ : TokenType.SINGLE_LINE_COMMENT;
+ return new analyzer.CommentToken(type, token.value, token.charOffset);
+ }
- void matchGroups(Token token, analyzer.Token translatedToken) {
- if (identical(endTokenStack.last, token)) {
- beginTokenStack.last.endToken = translatedToken;
- beginTokenStack.removeLast();
- endTokenStack.removeLast();
+ /// Translates a single fasta non-comment token to the corresponding analyzer
+ /// token.
+ ///
+ /// [precedingComments] is not `null`, the translated token is pointed to it.
+ analyzer.Token translateToken(
+ Token token, analyzer.CommentToken precedingComments) =>
+ toAnalyzerToken(token, precedingComments);
+
+ /// Creates appropriate begin/end token links based on the fact that [token]
+ /// was translated to [translatedToken].
+ ///
+ /// Background: both fasta and analyzer have links from a "BeginToken" to its
+ /// matching "EndToken" in a group (like parentheses and braces). However,
+ /// fasta may contain synthetic tokens from error recovery that are not mapped
+ /// to the analyzer token stream. We use [_beginTokenStack] and
+ /// [_endTokenStack] to create the appropriate links for non-synthetic tokens
+ /// in the way analyzer expects.
ahe 2017/02/15 22:09:20 FYI: If you haven't noticed it already: there's do
Paul Berry 2017/02/16 02:01:23 Thanks for the pointer!
+ void _matchGroups(Token token, analyzer.Token translatedToken) {
+ if (identical(_endTokenStack.last, token)) {
+ _beginTokenStack.last.endToken = translatedToken;
+ _beginTokenStack.removeLast();
+ _endTokenStack.removeLast();
}
// Synthetic end tokens use the same offset as the begin token.
if (translatedToken is analyzer.BeginToken &&
token is BeginGroupToken &&
token.endGroup != null &&
token.endGroup.charOffset != token.charOffset) {
- beginTokenStack.add(translatedToken);
- endTokenStack.add(token.endGroup);
+ _beginTokenStack.add(translatedToken);
+ _endTokenStack.add(token.endGroup);
}
}
- while (true) {
- if (token.info.kind == BAD_INPUT_TOKEN) {
- ErrorToken errorToken = token;
- _translateErrorToken(errorToken, reportError);
- } else if (token.info.kind == COMMENT_TOKEN) {
- // TODO(paulberry,ahe): It would be nice if the scanner gave us an
- // easier way to distinguish between the two types of comment.
- var type = token.value.startsWith('/*')
- ? TokenType.MULTI_LINE_COMMENT
- : TokenType.SINGLE_LINE_COMMENT;
- var translatedToken =
- new analyzer.CommentToken(type, token.value, token.charOffset);
- if (currentCommentHead == null) {
- currentCommentHead = currentCommentTail = translatedToken;
- } else {
- currentCommentTail.setNext(translatedToken);
- currentCommentTail = translatedToken;
+ /// Translates the given error [token] into an analyzer error and reports it
+ /// using [reportError].
+ void _translateErrorToken(ErrorToken token) {
+ int charOffset = token.charOffset;
+ // TODO(paulberry,ahe): why is endOffset sometimes null?
+ int endOffset = token.endOffset ?? charOffset;
+ void _makeError(
+ analyzer.ScannerErrorCode errorCode, List<Object> arguments) {
+ if (_isAtEnd(token, charOffset)) {
+ // Analyzer never generates an error message past the end of the input,
+ // since such an error would not be visible in an editor.
+ // TODO(paulberry,ahe): would it make sense to replicate this behavior
+ // in fasta, or move it elsewhere in analyzer?
+ charOffset--;
}
- } else {
- var translatedToken = toAnalyzerToken(token, currentCommentHead);
- matchGroups(token, translatedToken);
- translatedToken.setNext(translatedToken);
- currentCommentHead = currentCommentTail = null;
- analyzerTokenTail.setNext(translatedToken);
- translatedToken.previous = analyzerTokenTail;
- analyzerTokenTail = translatedToken;
+ reportError(errorCode, charOffset, arguments);
}
- if (token.isEof) {
- return analyzerTokenHead.next;
+
+ var errorCode = token.errorCode;
+ switch (errorCode) {
+ case ErrorKind.UnterminatedString:
+ // TODO(paulberry,ahe): Fasta reports the error location as the entire
+ // string; analyzer expects the end of the string.
+ charOffset = endOffset;
+ return _makeError(
+ analyzer.ScannerErrorCode.UNTERMINATED_STRING_LITERAL, null);
+ case ErrorKind.UnmatchedToken:
+ return null;
+ case ErrorKind.UnterminatedComment:
+ // TODO(paulberry,ahe): Fasta reports the error location as the entire
+ // comment; analyzer expects the end of the comment.
+ charOffset = endOffset;
+ return _makeError(
+ analyzer.ScannerErrorCode.UNTERMINATED_MULTI_LINE_COMMENT, null);
+ case ErrorKind.MissingExponent:
+ // TODO(paulberry,ahe): Fasta reports the error location as the entire
+ // number; analyzer expects the end of the number.
+ charOffset = endOffset;
+ return _makeError(analyzer.ScannerErrorCode.MISSING_DIGIT, null);
+ case ErrorKind.ExpectedHexDigit:
+ // TODO(paulberry,ahe): Fasta reports the error location as the entire
+ // number; analyzer expects the end of the number.
+ charOffset = endOffset;
+ return _makeError(analyzer.ScannerErrorCode.MISSING_HEX_DIGIT, null);
+ case ErrorKind.NonAsciiIdentifier:
+ case ErrorKind.NonAsciiWhitespace:
+ return _makeError(
+ analyzer.ScannerErrorCode.ILLEGAL_CHARACTER, [token.character]);
+ case ErrorKind.UnexpectedDollarInString:
+ return null;
+ default:
+ throw new UnimplementedError('$errorCode');
}
- token = token.next;
}
}
@@ -386,63 +495,6 @@ bool _isAtEnd(Token token, int charOffset) {
}
}
-/// Translates the given error [token] into an analyzer error and reports it
-/// using [reportError].
-void _translateErrorToken(
- ErrorToken token,
- void reportError(analyzer.ScannerErrorCode errorCode, int offset,
- List<Object> arguments)) {
- int charOffset = token.charOffset;
- // TODO(paulberry,ahe): why is endOffset sometimes null?
- int endOffset = token.endOffset ?? charOffset;
- void _makeError(analyzer.ScannerErrorCode errorCode, List<Object> arguments) {
- if (_isAtEnd(token, charOffset)) {
- // Analyzer never generates an error message past the end of the input,
- // since such an error would not be visible in an editor.
- // TODO(paulberry,ahe): would it make sense to replicate this behavior
- // in fasta, or move it elsewhere in analyzer?
- charOffset--;
- }
- reportError(errorCode, charOffset, arguments);
- }
-
- var errorCode = token.errorCode;
- switch (errorCode) {
- case ErrorKind.UnterminatedString:
- // TODO(paulberry,ahe): Fasta reports the error location as the entire
- // string; analyzer expects the end of the string.
- charOffset = endOffset;
- return _makeError(
- analyzer.ScannerErrorCode.UNTERMINATED_STRING_LITERAL, null);
- case ErrorKind.UnmatchedToken:
- return null;
- case ErrorKind.UnterminatedComment:
- // TODO(paulberry,ahe): Fasta reports the error location as the entire
- // comment; analyzer expects the end of the comment.
- charOffset = endOffset;
- return _makeError(
- analyzer.ScannerErrorCode.UNTERMINATED_MULTI_LINE_COMMENT, null);
- case ErrorKind.MissingExponent:
- // TODO(paulberry,ahe): Fasta reports the error location as the entire
- // number; analyzer expects the end of the number.
- charOffset = endOffset;
- return _makeError(analyzer.ScannerErrorCode.MISSING_DIGIT, null);
- case ErrorKind.ExpectedHexDigit:
- // TODO(paulberry,ahe): Fasta reports the error location as the entire
- // number; analyzer expects the end of the number.
- charOffset = endOffset;
- return _makeError(analyzer.ScannerErrorCode.MISSING_HEX_DIGIT, null);
- case ErrorKind.NonAsciiIdentifier:
- case ErrorKind.NonAsciiWhitespace:
- return _makeError(
- analyzer.ScannerErrorCode.ILLEGAL_CHARACTER, [token.character]);
- case ErrorKind.UnexpectedDollarInString:
- return null;
- default:
- throw new UnimplementedError('$errorCode');
- }
-}
-
analyzer.Token toAnalyzerToken(Token token,
[analyzer.CommentToken commentToken]) {
if (token == null) return null;
« no previous file with comments | « no previous file | pkg/front_end/test/scanner_fasta_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698