blob: d60ec032d0824641dd830a561cc35968391fdbd2 [file] [edit]
/*
* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
* Use of this file is governed by the BSD 3-clause license that
* can be found in the LICENSE.txt file in the project root.
*/
import 'input_stream.dart';
import 'interval_set.dart';
import 'misc/misc.dart';
import 'token_source.dart';
/// A token has properties: text, type, line, character position in the line
/// (so we can ignore tabs), token channel, index, and source from which
/// we obtained this token.
abstract class Token {
static const int INVALID_TYPE = 0;
/// During lookahead operations, this "token" signifies we hit rule end ATN state
/// and did not follow it despite needing to.
static const int EPSILON = -2;
static const int MIN_USER_TOKEN_TYPE = 1;
static const int EOF = IntStream.EOF;
/// All tokens go to the parser (unless skip() is called in that rule)
/// on a particular "channel". The parser tunes to a particular channel
/// so that whitespace etc... can go to the parser on a "hidden" channel.
static const int DEFAULT_CHANNEL = 0;
/// Anything on different channel than DEFAULT_CHANNEL is not parsed
/// by parser.
static const int HIDDEN_CHANNEL = 1;
/// This is the minimum constant value which can be assigned to a
/// user-defined token channel.
///
/// <p>
/// The non-negative numbers less than {@link #MIN_USER_CHANNEL_VALUE} are
/// assigned to the predefined channels {@link #DEFAULT_CHANNEL} and
/// {@link #HIDDEN_CHANNEL}.</p>
///
/// @see Token#getChannel()
static const int MIN_USER_CHANNEL_VALUE = 2;
/// Get the text of the token.
String? get text;
/// Get the token type of the token */
int get type;
/// The line number on which the 1st character of this token was matched,
/// line=1..n
int? get line;
/// The index of the first character of this token relative to the
/// beginning of the line at which it occurs, 0..n-1
int get charPositionInLine;
/// Return the channel this token. Each token can arrive at the parser
/// on a different channel, but the parser only "tunes" to a single channel.
/// The parser ignores everything not on DEFAULT_CHANNEL.
int get channel;
/// An index from 0..n-1 of the token object in the input stream.
/// This must be valid in order to print token streams and
/// use TokenRewriteStream.
///
/// Return -1 to indicate that this token was conjured up since
/// it doesn't have a valid index.
int get tokenIndex;
/// The starting character index of the token
/// This method is optional; return -1 if not implemented.
int get startIndex;
/// The last character index of the token.
/// This method is optional; return -1 if not implemented.
int get stopIndex;
/// Gets the [TokenSource] which created this token.
TokenSource? get tokenSource;
/// Gets the [CharStream] from which this token was derived.
CharStream? get inputStream;
}
abstract class WritableToken extends Token {
set text(String? text);
set type(int ttype);
set line(int? line);
set charPositionInLine(int pos);
set channel(int channel);
set tokenIndex(int index);
}
class CommonToken extends WritableToken {
/// An empty [Pair] which is used as the default value of
/// {@link #source} for tokens that do not have a source.
static const EMPTY_SOURCE = Pair<TokenSource?, CharStream?>(null, null);
@override
int type;
@override
int? line;
@override
int charPositionInLine = -1; // set to invalid position
@override
int channel = Token.DEFAULT_CHANNEL;
/// These properties share a field to reduce the memory footprint of
/// [CommonToken]. Tokens created by a [CommonTokenFactory] from
/// the same source and input stream share a reference to the same
/// [Pair] containing these values.</p>
late Pair<TokenSource?, CharStream?> source;
/// This is the backing field for {@link #getText} when the token text is
/// explicitly set in the constructor or via {@link #setText}.
///
/// @see #getText()
String? _text;
@override
int tokenIndex = -1;
@override
int startIndex;
@override
int stopIndex;
/// Constructs a new [CommonToken] with the specified token type and
/// text.
///
/// @param type The token type.
/// @param text The text of the token.
CommonToken(
this.type, {
this.source = EMPTY_SOURCE,
this.channel = Token.DEFAULT_CHANNEL,
this.startIndex = -1,
this.stopIndex = -1,
text,
}) {
_text = text;
if (source.a != null) {
line = source.a!.line;
charPositionInLine = source.a!.charPositionInLine;
}
}
/// Constructs a new [CommonToken] as a copy of another [Token].
///
/// <p>
/// If [oldToken] is also a [CommonToken] instance, the newly
/// constructed token will share a reference to the {@link #text} field and
/// the [Pair] stored in {@link #source}. Otherwise, {@link #text} will
/// be assigned the result of calling {@link #getText}, and {@link #source}
/// will be constructed from the result of {@link Token#getTokenSource} and
/// {@link Token#getInputStream}.</p>
///
/// @param oldToken The token to copy.
CommonToken.copy(Token oldToken)
: type = oldToken.type,
line = oldToken.line,
tokenIndex = oldToken.tokenIndex,
charPositionInLine = oldToken.charPositionInLine,
channel = oldToken.channel,
startIndex = oldToken.startIndex,
stopIndex = oldToken.stopIndex {
if (oldToken is CommonToken) {
_text = oldToken.text;
source = oldToken.source;
} else {
_text = oldToken.text;
source = Pair<TokenSource?, CharStream?>(
oldToken.tokenSource,
oldToken.inputStream,
);
}
}
@override
String? get text {
if (_text != null) {
return _text;
}
final input = inputStream;
if (input == null) return null;
final n = input.size;
if (startIndex < n && stopIndex < n) {
return input.getText(Interval.of(startIndex, stopIndex));
} else {
return '<EOF>';
}
}
/// Explicitly set the text for this token. If {code text} is not
/// null, then {@link #getText} will return this value rather than
/// extracting the text from the input.
///
/// @param text The explicit text of the token, or null if the text
/// should be obtained from the input along with the start and stop indexes
/// of the token.
@override
set text(String? text) {
_text = text;
}
@override
TokenSource? get tokenSource {
return source.a;
}
@override
CharStream? get inputStream {
return source.b;
}
@override
String toString([void _]) {
var txt = text;
if (txt != null) {
txt = txt
.replaceAll('\n', r'\n')
.replaceAll('\r', r'\r')
.replaceAll('\t', r'\t');
} else {
txt = '<no text>';
}
return "[@$tokenIndex,$startIndex:$stopIndex='$txt',<$type>" +
(channel > 0 ? ',channel=$channel' : '') +
',$line:$charPositionInLine]';
}
}
/// A [Token] object representing an entire subtree matched by a parser
/// rule; e.g., {@code <expr>}. These tokens are created for [TagChunk]
/// chunks where the tag corresponds to a parser rule.
class RuleTagToken implements Token {
/// Gets the name of the rule associated with this rule tag.
///
/// @return The name of the parser rule associated with this rule tag.
final String ruleName;
/// The token type for the current token. This is the token type assigned to
/// the bypass alternative for the rule during ATN deserialization.
final int bypassTokenType;
/// Gets the label associated with the rule tag.
///
/// @return The name of the label associated with the rule tag, or
/// null if this is an unlabeled rule tag.
final String? label;
/// Constructs a new instance of [RuleTagToken] with the specified rule
/// name, bypass token type, and label.
///
/// @param ruleName The name of the parser rule this rule tag matches.
/// @param bypassTokenType The bypass token type assigned to the parser rule.
/// @param label The label associated with the rule tag, or null if
/// the rule tag is unlabeled.
///
/// @exception ArgumentError.value(value) if [ruleName] is null
/// or empty.
RuleTagToken(this.ruleName, this.bypassTokenType, [this.label]) {
if (ruleName.isEmpty) {
throw ArgumentError.value(
ruleName,
'ruleName',
'cannot be empty.',
);
}
}
/// {@inheritDoc}
///
/// <p>Rule tag tokens are always placed on the {@link #DEFAULT_CHANNEL}.</p>
@override
int get channel {
return Token.DEFAULT_CHANNEL;
}
/// {@inheritDoc}
///
/// <p>This method returns the rule tag formatted with {@code <} and {@code >}
/// delimiters.</p>
@override
String get text {
if (label != null) {
return '<' + label! + ':' + ruleName + '>';
}
return '<' + ruleName + '>';
}
/// {@inheritDoc}
///
/// <p>Rule tag tokens have types assigned according to the rule bypass
/// transitions created during ATN deserialization.</p>
@override
int get type {
return bypassTokenType;
}
/// {@inheritDoc}
///
/// <p>The implementation for [RuleTagToken] always returns 0.</p>
@override
int get line {
return 0;
}
/// {@inheritDoc}
///
/// <p>The implementation for [RuleTagToken] always returns -1.</p>
@override
int get charPositionInLine {
return -1;
}
/// {@inheritDoc}
///
/// <p>The implementation for [RuleTagToken] always returns -1.</p>
@override
int get tokenIndex {
return -1;
}
/// {@inheritDoc}
///
/// <p>The implementation for [RuleTagToken] always returns -1.</p>
@override
int get startIndex {
return -1;
}
/// {@inheritDoc}
///
/// <p>The implementation for [RuleTagToken] always returns -1.</p>
@override
int get stopIndex {
return -1;
}
/// {@inheritDoc}
///
/// <p>The implementation for [RuleTagToken] always returns null.</p>
@override
TokenSource? get tokenSource {
return null;
}
/// {@inheritDoc}
///
/// <p>The implementation for [RuleTagToken] always returns null.</p>
@override
CharStream? get inputStream {
return null;
}
/// {@inheritDoc}
///
/// <p>The implementation for [RuleTagToken] returns a string of the form
/// {@code ruleName:bypassTokenType}.</p>
@override
String toString() {
return ruleName + ':$bypassTokenType';
}
}
/// A [Token] object representing a token of a particular type; e.g.,
/// {@code <ID>}. These tokens are created for [TagChunk] chunks where the
/// tag corresponds to a lexer rule or token type.
class TokenTagToken extends CommonToken {
/// Gets the token name.
/// @return The token name.
final String tokenName;
/// Gets the label associated with the rule tag.
///
/// @return The name of the label associated with the rule tag, or
/// null if this is an unlabeled rule tag.
final String? label;
/// Constructs a new instance of [TokenTagToken] with the specified
/// token name, type, and label.
///
/// @param tokenName The token name.
/// @param type The token type.
/// @param label The label associated with the token tag, or null if
/// the token tag is unlabeled.
TokenTagToken(this.tokenName, type, [this.label]) : super(type);
/// {@inheritDoc}
///
/// <p>The implementation for [TokenTagToken] returns the token tag
/// formatted with {@code <} and {@code >} delimiters.</p>
@override
String get text {
if (label != null) {
return '<' + label! + ':' + tokenName + '>';
}
return '<' + tokenName + '>';
}
/// {@inheritDoc}
///
/// <p>The implementation for [TokenTagToken] returns a string of the form
/// {@code tokenName:type}.</p>
@override
String toString([void _]) {
return tokenName + ':$type';
}
}