blob: 4329810d4bd9a8efc2b91cee37f914fc8c063ebc [file] [log] [blame]
// 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 fasta.parser.util;
import 'package:kernel/ast.dart' show TreeNode;
import '../fasta_codes.dart' show noLength;
import '../scanner.dart' show Token;
import '../../scanner/token.dart' show BeginToken;
/// 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 == 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;
}
/// A null-aware alternative to `token.offset`. If [token] is `null`, returns
/// `TreeNode.noOffset`.
int offsetForToken(Token token) {
return token == null ? TreeNode.noOffset : token.offset;
}
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;
}