00c2962db8
Change-Id: I4fa87aee65f30a9868a6cf8f0342591869ece7ea Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/123663 Reviewed-by: Paul Berry <paulberry@google.com> Commit-Queue: Johnni Winther <johnniwinther@google.com>
208 lines
6.7 KiB
Dart
208 lines
6.7 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 '../messages/codes.dart' show noLength;
|
|
|
|
import '../scanner/scanner.dart' show Token;
|
|
|
|
import '../scanner/token.dart'
|
|
show BeginToken, SimpleToken, SyntheticToken, 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;
|
|
|
|
/// Return true if the given token matches one of the given values.
|
|
bool isOneOf(Token token, Iterable<String> values) {
|
|
for (String tokenValue in values) {
|
|
if (optional(tokenValue, token)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Return true if the given token matches one of the given values or is EOF.
|
|
bool isOneOfOrEof(Token token, Iterable<String> values) {
|
|
for (String tokenValue in values) {
|
|
if (optional(tokenValue, token)) {
|
|
return true;
|
|
}
|
|
}
|
|
return token.isEof;
|
|
}
|
|
|
|
/// 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(optional('@', token));
|
|
Token next = token.next;
|
|
if (next.isIdentifier) {
|
|
token = next;
|
|
next = token.next;
|
|
while (optional('.', next)) {
|
|
token = next;
|
|
next = token.next;
|
|
if (next.isIdentifier) {
|
|
token = next;
|
|
next = token.next;
|
|
}
|
|
}
|
|
if (optional('(', next) && !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(optional('>=', token));
|
|
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(optional('>>', token));
|
|
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(optional('>>=', token));
|
|
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(optional('>>=', token));
|
|
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(optional('>>>', token));
|
|
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(optional('>>>=', token));
|
|
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);
|
|
}
|
|
|
|
/// 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;
|
|
}
|