blob: cf298ecc02c689e4e0f718adeb2312d9ff183b36 [file] [log] [blame]
package ANTLR::Runtime::CommonTokenStream;
use Carp;
use Readonly;
use UNIVERSAL qw( isa );
use ANTLR::Runtime::CharStream;
use ANTLR::Runtime::Token;
use ANTLR::Runtime::TokenSource;
use Moose;
use overload
'""' => \&str
;
with 'ANTLR::Runtime::IntStream',
'ANTLR::Runtime::TokenStream';
has 'token_source' => (
is => 'rw',
does => 'ANTLR::Runtime::TokenSource',
);
has 'tokens' => (
is => 'rw',
isa => 'ArrayRef[ANTLR::Runtime::Token]',
default => sub { [] },
);
has 'channel_override_map' => (
is => 'rw',
isa => 'HashRef[Int]',
);
has 'discard_set' => (
is => 'rw',
isa => 'HashRef[Int]',
);
has 'channel' => (
is => 'rw',
isa => 'Int',
default => ANTLR::Runtime::Token->DEFAULT_CHANNEL,
);
has 'discard_off_channel_tokens' => (
is => 'rw',
isa => 'Bool',
default => 0,
);
has 'last_marker' => (
is => 'rw',
isa => 'Int',
default => 0,
);
has 'p' => (
is => 'rw',
isa => 'Int',
default => -1,
);
sub set_token_source {
my ($self, $token_source) = @_;
$self->token_source($token_source);
$self->tokens([]);
$self->p(-1);
$self->channel(ANTLR::Runtime::Token->DEFAULT_CHANNEL);
}
sub fill_buffer {
my ($self) = @_;
my $index = 0;
my $t = $self->token_source->next_token();
while (defined $t && $t->get_type() != ANTLR::Runtime::CharStream->EOF) {
my $discard = 0;
# is there a channel override for token type?
if (defined $self->channel_override_map) {
my $channel = $self->channel_override_map->{$t->get_type()};
if (defined $channel) {
$t->set_channel($channel);
}
}
if (defined $self->discard_set && $self->discard_set->contains($t->get_type())) {
$discard = 1;
} elsif ($self->discard_off_channel_tokens && $t->get_channel() != $self->channel) {
$discard = 1;
}
if (!$discard) {
$t->set_token_index($index);
push @{$self->tokens}, $t;
++$index;
}
} continue {
$t = $self->token_source->next_token();
}
# leave p pointing at first token on channel
$self->p(0);
$self->skip_off_token_channels($self->p);
}
sub consume {
my ($self) = @_;
if ($self->p < @{$self->tokens}) {
$self->p($self->p + 1);
$self->p($self->skip_off_token_channels($self->p)); # leave p on valid token
}
}
sub skip_off_token_channels {
my ($self, $i) = @_;
my $n = @{$self->tokens};
while ($i < $n && $self->tokens->[$i]->get_channel() != $self->channel) {
++$i;
}
return $i;
}
sub skip_off_token_channels_reverse {
my ($self, $i) = @_;
while ($i >= 0 && $self->tokens->[$i]->get_channel() != $self->channel) {
--$i;
}
return $i;
}
sub set_token_type_channel {
my ($self, $ttype, $channel) = @_;
if (!defined $self->channel_override_map) {
$self->channel_override_map({});
}
$self->channel_override_map->{$ttype} = $channel;
}
sub discard_token_type {
my ($self, $ttype) = @_;
if (!defined $self->discard_set) {
$self->discard_set({});
}
$self->discard_set->{$ttype} = 1;
}
sub get_tokens {
my ($self, $args) = @_;
if ($self->p == -1) {
$self->fill_buffer();
}
if (!defined $args) {
return $self->tokens;
}
my $start = $args->{start};
my $stop = $args->{stop};
my $types;
if (exists $args->{types}) {
if (ref $args->{types} eq 'ARRAY') {
$types = ANTLR::Runtime::BitSet->new($args->{types});
} else {
$types = $args->{types};
}
} else {
my $ttype = $args->{ttype};
$types = ANTLR::Runtime::BitSet->of($ttype);
}
if ($stop >= @{$self->tokens}) {
$stop = $#{$self->tokens};
}
if ($start < 0) {
$start = 0;
}
if ($start > $stop) {
return undef;
}
my $filtered_tokens = [];
foreach my $t (@{$self->tokens}[$start..$stop]) {
if (!defined $types || $types->member($t->get_type())) {
push @$filtered_tokens, $t;
}
}
if (!@{$filtered_tokens}) {
$filtered_tokens = undef;
}
return $filtered_tokens;
}
sub LT {
my ($self, $k) = @_;
if ($self->p == -1) {
$self->fill_buffer();
}
if ($k == 0) {
return undef;
}
if ($k < 0) {
return $self->LB(-$k);
}
if ($self->p + $k - 1 >= @{$self->tokens}) {
return ANTLR::Runtime::Token->EOF_TOKEN;
}
my $i = $self->p;
my $n = 1;
while ($n < $k) {
$i = $self->skip_off_token_channels($i+1);
++$n;
}
if ($i >= @{$self->tokens}) {
return ANTLR::Runtime::Token->EOF_TOKEN;
}
return $self->tokens->[$i];
}
sub LB {
my ($self, $k) = @_;
if ($self->p == -1) {
$self->fill_buffer();
}
if ($k == 0) {
return undef;
}
if ($self->p - $k < 0) {
return undef;
}
my $i = $self->p;
my $n = 1;
while ($n <= $k) {
$k = $self->skip_off_token_channels_reverse($i - 1);
++$n;
}
if ($i < 0) {
return undef;
}
return $self->tokens->[$i];
}
sub get {
my ($self, $i) = @_;
return $self->tokens->[$i];
}
sub LA {
my ($self, $i) = @_;
return $self->LT($i)->get_type();
}
sub mark {
my ($self) = @_;
if ($self->p == -1) {
$self->fill_buffer();
}
$self->last_marker($self->index());
return $self->last_marker;
}
sub release {
my ($self, $marker) = @_;
# no resources to release
}
sub size {
my ($self) = @_;
return scalar @{$self->tokens};
}
sub index {
my ($self) = @_;
return $self->p;
}
sub rewind {
Readonly my $usage => 'void rewind(int marker) | void rewind()';
croak $usage if @_ != 1 && @_ != 2;
if (@_ == 1) {
my ($self) = @_;
$self->seek($self->last_marker);
} else {
my ($self, $marker) = @_;
$self->seek($marker);
}
}
sub seek {
my ($self, $index) = @_;
$self->p($index);
}
sub get_token_source {
my ($self) = @_;
return $self->token_source;
}
sub get_source_name {
my ($self) = @_;
return $self->get_token_source()->get_source_name();
}
sub str {
my ($self) = @_;
return $self->to_string();
}
sub to_string {
Readonly my $usage => 'String to_string() | String to_string(int start, int stop | String to_string(Token start, Token stop)';
croak $usage if @_ != 1 && @_ != 3;
if (@_ == 1) {
my ($self) = @_;
if ($self->p == -1) {
$self->fill_buffer();
}
return $self->to_string(0, $#{$self->tokens});
} else {
my ($self, $start, $stop) = @_;
if (defined $start && defined $stop) {
if (ref($start) && $start->isa('ANTLR::Runtime::Token')) {
$start = $start->get_token_index();
}
if (ref($start) && $stop->isa('ANTLR::Runtime::Token')) {
$stop = $stop->get_token_index();
}
if ($start < 0 || $stop < 0) {
return undef;
}
if ($self->p == -1) {
$self->fill_buffer();
}
if ($stop >= @{$self->tokens}) {
$stop = $#{$self->tokens};
}
my $buf = '';
foreach my $t (@{$self->tokens}[$start..$stop]) {
$buf .= $t->get_text();
}
return $buf;
} else {
return undef;
}
}
}
no Moose;
__PACKAGE__->meta->make_immutable();
1;
__END__