| // Copyright (c) 2011, 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. |
| |
| library dart2js.scanner.string_scanner; |
| |
| import 'characters.dart' show $CR, $EOF, $LF, $SPACE; |
| |
| import 'internal_utils.dart' show isIdentifierChar; |
| |
| import 'token.dart' |
| show |
| CommentToken, |
| LanguageVersionToken, |
| SyntheticStringToken, |
| Token, |
| TokenType; |
| |
| import 'token.dart' as analyzer show StringToken; |
| |
| import 'abstract_scanner.dart' |
| show AbstractScanner, LanguageVersionChanged, ScannerConfiguration; |
| |
| import 'string_canonicalizer.dart' |
| show canonicalizeString, canonicalizeSubString; |
| |
| import 'token_impl.dart' |
| show |
| CommentTokenImpl, |
| DartDocToken, |
| LanguageVersionTokenImpl, |
| StringTokenImpl; |
| |
| import 'error_token.dart' show ErrorToken; |
| |
| /** |
| * Scanner that reads from a String and creates tokens that points to |
| * substrings. |
| */ |
| class StringScanner extends AbstractScanner { |
| /** The file content. */ |
| final String _string; |
| final int _stringLengthMinusOne; |
| |
| /** The current offset in [_string]. */ |
| @override |
| int scanOffset = -1; |
| |
| StringScanner( |
| this._string, { |
| ScannerConfiguration? configuration, |
| bool includeComments = false, |
| LanguageVersionChanged? languageVersionChanged, |
| }) : _stringLengthMinusOne = _string.length - 1, |
| super( |
| configuration, |
| includeComments, |
| languageVersionChanged, |
| numberOfBytesHint: _string.length, |
| ); |
| |
| StringScanner.recoveryOptionScanner(StringScanner super.copyFrom) |
| : _string = copyFrom._string, |
| _stringLengthMinusOne = copyFrom._stringLengthMinusOne, |
| scanOffset = copyFrom.scanOffset, |
| super.recoveryOptionScanner(); |
| |
| @override |
| StringScanner createRecoveryOptionScanner() { |
| return new StringScanner.recoveryOptionScanner(this); |
| } |
| |
| static bool isLegalIdentifier(String identifier) { |
| StringScanner scanner = new StringScanner(identifier); |
| Token startToken = scanner.tokenize(); |
| return startToken is! ErrorToken && startToken.next!.isEof; |
| } |
| |
| @override |
| @pragma('vm:unsafe:no-bounds-checks') |
| int advance() { |
| // Always increment so scanOffset goes past the end. |
| ++scanOffset; |
| if (scanOffset > _stringLengthMinusOne) return $EOF; |
| return _string.codeUnitAt(scanOffset); |
| } |
| |
| @override |
| @pragma('vm:unsafe:no-bounds-checks') |
| int current() { |
| if (scanOffset > _stringLengthMinusOne) return $EOF; |
| return _string.codeUnitAt(scanOffset); |
| } |
| |
| @override |
| int passIdentifierCharAllowDollar() { |
| while (true) { |
| int next = advance(); |
| if (!isIdentifierChar(next, /* allowDollar = */ true)) { |
| return next; |
| } |
| } |
| } |
| |
| @override |
| bool scanUntilLineEnd() { |
| bool asciiOnly = true; |
| int next = advance(); |
| while (true) { |
| if (next > 127) asciiOnly = false; |
| if ($LF == next || $CR == next || $EOF == next) { |
| return asciiOnly; |
| } |
| next = advance(); |
| } |
| } |
| |
| @override |
| @pragma("vm:prefer-inline") |
| int skipSpaces() { |
| int next = advance(); |
| // Sequences of spaces are common, so advance through them fast. |
| while (next == $SPACE) { |
| // We don't invoke [:appendWhiteSpace(next):] here for efficiency, |
| // assuming that it does not do anything for space characters. |
| next = advance(); |
| } |
| return next; |
| } |
| |
| @override |
| @pragma('vm:unsafe:no-bounds-checks') |
| int peek() { |
| int next = scanOffset + 1; |
| if (next > _stringLengthMinusOne) return $EOF; |
| return _string.codeUnitAt(next); |
| } |
| |
| @override |
| int get stringOffset => scanOffset; |
| |
| @override |
| int currentAsUnicode(int next) => next; |
| |
| @override |
| void handleUnicode(int startScanOffset) {} |
| |
| @override |
| analyzer.StringToken createSubstringToken( |
| TokenType type, |
| int start, |
| bool asciiOnly, |
| int extraOffset, |
| bool allowLazy, |
| ) { |
| return new StringTokenImpl.fromSubstring( |
| type, |
| _string, |
| start, |
| scanOffset + extraOffset, |
| tokenStart, |
| canonicalize: true, |
| precedingComments: comments, |
| allowLazy: allowLazy, |
| ); |
| } |
| |
| @override |
| analyzer.StringToken createSyntheticSubstringToken( |
| TokenType type, |
| int start, |
| bool asciiOnly, |
| String syntheticChars, |
| ) { |
| String value = |
| syntheticChars.length == 0 |
| ? canonicalizeSubString(_string, start, scanOffset) |
| : canonicalizeString( |
| _string.substring(start, scanOffset) + syntheticChars, |
| ); |
| return new SyntheticStringToken( |
| type, |
| value, |
| tokenStart, |
| value.length - syntheticChars.length, |
| ); |
| } |
| |
| @override |
| CommentToken createCommentToken( |
| TokenType type, |
| int start, |
| bool asciiOnly, [ |
| int extraOffset = 0, |
| ]) { |
| return new CommentTokenImpl.fromSubstring( |
| type, |
| _string, |
| start, |
| scanOffset + extraOffset, |
| tokenStart, |
| canonicalize: true, |
| ); |
| } |
| |
| @override |
| DartDocToken createDartDocToken( |
| TokenType type, |
| int start, |
| bool asciiOnly, [ |
| int extraOffset = 0, |
| ]) { |
| return new DartDocToken.fromSubstring( |
| type, |
| _string, |
| start, |
| scanOffset + extraOffset, |
| tokenStart, |
| canonicalize: true, |
| ); |
| } |
| |
| @override |
| LanguageVersionToken createLanguageVersionToken( |
| int start, |
| int major, |
| int minor, |
| ) { |
| return new LanguageVersionTokenImpl.fromSubstring( |
| _string, |
| start, |
| scanOffset, |
| tokenStart, |
| major, |
| minor, |
| canonicalize: true, |
| ); |
| } |
| |
| @override |
| // This class used to enforce zero-terminated input, so we only return true |
| // once advance has been out of bounds. |
| // TODO(jensj): This should probably change. |
| // It's at least used in tests (where the eof token has its offset reduced |
| // by one to 'fix' this.) |
| bool atEndOfFile() => scanOffset > _stringLengthMinusOne; |
| } |