blob: 0fd08af667a3dd1eda15cb6e5d81b2ef87692fdf [file] [log] [blame]
// 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;
}