bce3dd4fe7
Autogenerated files was formatted with the new style, so if one for instance formatted the file via the IDE or by saying `out/ReleaseX64/dart-sdk/bin/dart format pkg/front_end/lib/` we'd get *a lot* of changes which isn't great. This CL sets the formatter version for the auto-generated files, hopefully avoiding such things. Change-Id: I4f92aafde7c77e7c78179f78bf821979a25ec12c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/398884 Reviewed-by: Johnni Winther <johnniwinther@google.com> Commit-Queue: Jens Johansen <jensj@google.com>
287 lines
9.5 KiB
Dart
287 lines
9.5 KiB
Dart
// Copyright (c) 2017, 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 _fe_analyzer_shared.parser.util;
|
|
|
|
import 'dart:typed_data';
|
|
|
|
import '../messages/codes.dart' show noLength;
|
|
|
|
import '../scanner/scanner.dart' show Keyword, Token;
|
|
|
|
import '../scanner/token.dart'
|
|
show BeginToken, SimpleToken, SyntheticToken, TokenIsAExtension, TokenType;
|
|
|
|
/// Returns true if [token] is the symbol or keyword [value].
|
|
bool optional(String value, Token token) {
|
|
return identical(value, token.stringValue);
|
|
}
|
|
|
|
/// Returns the token before the close brace, bracket, or parenthesis
|
|
/// associated with [left]. For '<', it may return `null`.
|
|
Token? beforeCloseBraceTokenFor(BeginToken left) {
|
|
Token? endToken = left.endToken;
|
|
if (endToken == null) {
|
|
return null;
|
|
}
|
|
Token token = left;
|
|
Token next = token.next!;
|
|
while (next != endToken && next != next.next) {
|
|
token = next;
|
|
next = token.next!;
|
|
}
|
|
return token;
|
|
}
|
|
|
|
/// Return [token] or a token before [token] which is either
|
|
/// not synthetic or synthetic with non-zero length.
|
|
Token findPreviousNonZeroLengthToken(Token token) {
|
|
while (token.isSynthetic && token.length == 0) {
|
|
Token? previous = token.beforeSynthetic;
|
|
if (previous == token) {
|
|
throw new StateError("token == token.beforeSynthetic");
|
|
}
|
|
if (previous == null) {
|
|
break;
|
|
}
|
|
token = previous;
|
|
}
|
|
return token;
|
|
}
|
|
|
|
/// Return [token] or a token after [token] which is either
|
|
/// not synthetic or synthetic with non-zero length.
|
|
/// This may return EOF if there are no more non-synthetic tokens in the stream.
|
|
Token findNonZeroLengthToken(Token token) {
|
|
while (token.isSynthetic && token.length == 0 && !token.isEof) {
|
|
token = token.next!;
|
|
}
|
|
return token;
|
|
}
|
|
|
|
bool isDigit(int c) => c >= 0x30 && c <= 0x39;
|
|
|
|
bool isLetter(int c) => c >= 0x41 && c <= 0x5A || c >= 0x61 && c <= 0x7A;
|
|
|
|
bool isLetterOrDigit(int c) => isLetter(c) || isDigit(c);
|
|
|
|
bool isWhitespace(int c) => c == 0x20 || c == 0xA || c == 0xD || c == 0x9;
|
|
|
|
bool isAnyOf(Token token, List<TokenType> values) {
|
|
TokenType type = token.type;
|
|
for (TokenType tokenValue in values) {
|
|
if (tokenValue == type) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// A null-aware alternative to `token.length`. If [token] is `null`, returns
|
|
/// [noLength].
|
|
int lengthForToken(Token? token) {
|
|
return token == null ? noLength : token.length;
|
|
}
|
|
|
|
/// Returns the length of the span from [begin] to [end] (inclusive). If both
|
|
/// tokens are null, return [noLength]. If one of the tokens are null, return
|
|
/// the length of the other token.
|
|
int lengthOfSpan(Token? begin, Token? end) {
|
|
if (begin == null) return lengthForToken(end);
|
|
if (end == null) return lengthForToken(begin);
|
|
return end.offset + end.length - begin.offset;
|
|
}
|
|
|
|
Token skipMetadata(Token token) {
|
|
token = token.next!;
|
|
assert(token.isA(TokenType.AT));
|
|
Token next = token.next!;
|
|
// Corresponds to 'ensureIdentifier' in [parseMetadata].
|
|
if (next.isIdentifier) {
|
|
token = next;
|
|
next = token.next!;
|
|
// Corresponds to 'parseQualifiedRestOpt' in [parseMetadata].
|
|
if (next.isA(TokenType.PERIOD)) {
|
|
token = next;
|
|
next = token.next!;
|
|
if (next.isIdentifier) {
|
|
token = next;
|
|
next = token.next!;
|
|
}
|
|
}
|
|
// Corresponds to 'computeTypeParamOrArg' in [parseMetadata].
|
|
if (next.isA(TokenType.LT) && !next.endGroup!.isSynthetic) {
|
|
token = next.endGroup!;
|
|
next = token.next!;
|
|
}
|
|
|
|
// The extra .identifier after arguments in [parseMetadata].
|
|
if (next.isA(TokenType.PERIOD)) {
|
|
token = next;
|
|
next = token.next!;
|
|
if (next.isIdentifier) {
|
|
token = next;
|
|
next = token.next!;
|
|
}
|
|
}
|
|
|
|
// Corresponds to 'parseArgumentsOpt' in [parseMetadata].
|
|
if (next.isA(TokenType.OPEN_PAREN) && !next.endGroup!.isSynthetic) {
|
|
token = next.endGroup!;
|
|
next = token.next!;
|
|
}
|
|
}
|
|
return token;
|
|
}
|
|
|
|
/// Split `>=` into two separate tokens.
|
|
/// Call [Token.setNext] to add the token to the stream.
|
|
Token splitGtEq(Token token) {
|
|
assert(token.isA(TokenType.GT_EQ));
|
|
return new SimpleToken(
|
|
TokenType.GT, token.charOffset, token.precedingComments)
|
|
..setNext(new SimpleToken(TokenType.EQ, token.charOffset + 1)
|
|
// Set next rather than calling Token.setNext
|
|
// so that the previous token is not set.
|
|
..next = token.next);
|
|
}
|
|
|
|
/// Split `>>` into two separate tokens.
|
|
/// Call [Token.setNext] to add the token to the stream.
|
|
SimpleToken splitGtGt(Token token) {
|
|
assert(token.isA(TokenType.GT_GT));
|
|
return new SimpleToken(
|
|
TokenType.GT, token.charOffset, token.precedingComments)
|
|
..setNext(new SimpleToken(TokenType.GT, token.charOffset + 1)
|
|
// Set next rather than calling Token.setNext
|
|
// so that the previous token is not set.
|
|
..next = token.next);
|
|
}
|
|
|
|
/// Split `>>=` into three separate tokens.
|
|
/// Call [Token.setNext] to add the token to the stream.
|
|
Token splitGtGtEq(Token token) {
|
|
assert(token.isA(TokenType.GT_GT_EQ));
|
|
return new SimpleToken(
|
|
TokenType.GT, token.charOffset, token.precedingComments)
|
|
..setNext(new SimpleToken(TokenType.GT, token.charOffset + 1)
|
|
..setNext(new SimpleToken(TokenType.EQ, token.charOffset + 2)
|
|
// Set next rather than calling Token.setNext
|
|
// so that the previous token is not set.
|
|
..next = token.next));
|
|
}
|
|
|
|
/// Split `>>=` into two separate tokens... `>` followed by `>=`.
|
|
/// Call [Token.setNext] to add the token to the stream.
|
|
Token splitGtFromGtGtEq(Token token) {
|
|
assert(token.isA(TokenType.GT_GT_EQ));
|
|
return new SimpleToken(
|
|
TokenType.GT, token.charOffset, token.precedingComments)
|
|
..setNext(new SimpleToken(TokenType.GT_EQ, token.charOffset + 1)
|
|
// Set next rather than calling Token.setNext
|
|
// so that the previous token is not set.
|
|
..next = token.next);
|
|
}
|
|
|
|
/// Split `>>>` into two separate tokens... `>` followed by `>>`.
|
|
/// Call [Token.setNext] to add the token to the stream.
|
|
Token splitGtFromGtGtGt(Token token) {
|
|
assert(token.isA(TokenType.GT_GT_GT));
|
|
return new SimpleToken(
|
|
TokenType.GT, token.charOffset, token.precedingComments)
|
|
..setNext(new SimpleToken(TokenType.GT_GT, token.charOffset + 1)
|
|
// Set next rather than calling Token.setNext
|
|
// so that the previous token is not set.
|
|
..next = token.next);
|
|
}
|
|
|
|
/// Split `>>>=` into two separate tokens... `>` followed by `>>=`.
|
|
/// Call [Token.setNext] to add the token to the stream.
|
|
Token splitGtFromGtGtGtEq(Token token) {
|
|
assert(token.isA(TokenType.GT_GT_GT_EQ));
|
|
return new SimpleToken(
|
|
TokenType.GT, token.charOffset, token.precedingComments)
|
|
..setNext(new SimpleToken(TokenType.GT_GT_EQ, token.charOffset + 1)
|
|
// Set next rather than calling Token.setNext
|
|
// so that the previous token is not set.
|
|
..next = token.next);
|
|
}
|
|
|
|
/// Strips separator characters (underscore) from [source].
|
|
///
|
|
/// No validation is performed on [source]; it could be a valid int, a valid
|
|
/// double, or invalid.
|
|
String stripSeparators(String source) {
|
|
Uint8List list = _separatorStripBuffer;
|
|
if (list.length < source.length - 1) {
|
|
// Looking at a very long number. Allocate a new buffer.
|
|
// We only strip separators after finding that there is at least one
|
|
// separator, so the length can be reduced by at least one character.
|
|
list = new Uint8List(source.length - 1);
|
|
if (list.length < 128) {
|
|
// Store the new, larger list as the reusable buffer.
|
|
_separatorStripBuffer = list;
|
|
}
|
|
}
|
|
|
|
int writeIndex = 0;
|
|
for (int i = 0; i < source.length; i++) {
|
|
int char = source.codeUnitAt(i);
|
|
if (char != 0x5f /* _ */) list[writeIndex++] = char;
|
|
}
|
|
return new String.fromCharCodes(list, 0, writeIndex);
|
|
}
|
|
|
|
/// A reusable buffer for stripping separators from number literals.
|
|
///
|
|
/// The majority of number literals fit in 24 characters. A maximal double with
|
|
/// no unnecessary leading or trailing zeros is 17 digits, one decimal point,
|
|
/// one 'e', two '-'s, and three exponent digits: 24 characters.
|
|
Uint8List _separatorStripBuffer = new Uint8List(24);
|
|
|
|
/// Return a synthetic `>` followed by [next].
|
|
/// Call [Token.setNext] to add the token to the stream.
|
|
Token syntheticGt(Token next) {
|
|
return new SyntheticToken(TokenType.GT, next.charOffset)
|
|
// Set next rather than calling Token.setNext
|
|
// so that the previous token is not set.
|
|
..next = next;
|
|
}
|
|
|
|
/// Returns the boolean value from a 'true' or 'false' [token].
|
|
bool boolFromToken(Token token) {
|
|
bool value = token.isA(Keyword.TRUE);
|
|
assert(value || token.isA(Keyword.FALSE));
|
|
return value;
|
|
}
|
|
|
|
/// Returns the integer value from an integer literal token.
|
|
///
|
|
/// If [hasSeparators], separator characters, '_', are stripped before parsing
|
|
/// the token text.
|
|
///
|
|
/// `null` is returned if the token text could not be parsed as an integer
|
|
/// value. This does _not_ mean that the token is not valid as an integer token
|
|
/// since negated integer literals are parsed as a unary operation on the
|
|
/// positive integer.
|
|
int? intFromToken(Token token, {required bool hasSeparators}) {
|
|
String text = token.lexeme;
|
|
if (hasSeparators) {
|
|
text = stripSeparators(text);
|
|
}
|
|
return int.tryParse(text);
|
|
}
|
|
|
|
/// Returns the double value from an double literal token.
|
|
///
|
|
/// If [hasSeparators], separator characters, '_', are stripped before parsing
|
|
/// the token text.
|
|
double doubleFromToken(Token token, {required bool hasSeparators}) {
|
|
String text = token.lexeme;
|
|
if (hasSeparators) {
|
|
text = stripSeparators(text);
|
|
}
|
|
return double.parse(text);
|
|
}
|