package ANTLR::Runtime::ANTLRStringStream; use Carp; use Readonly; use ANTLR::Runtime::CharStreamState; use Moose; with 'ANTLR::Runtime::IntStream', 'ANTLR::Runtime::CharStream'; has 'input' => ( is => 'ro', isa => 'Str', required => 1, ); has 'p' => ( is => 'rw', isa => 'Int', default => 0, ); has 'line' => ( is => 'rw', isa => 'Int', default => 1, ); has 'char_position_in_line' => ( is => 'rw', isa => 'Int', default => 0, ); has 'mark_depth' => ( is => 'rw', isa => 'Int', default => 0, ); has 'markers' => ( is => 'rw', isa => 'ArrayRef[Maybe[ANTLR::Runtime::CharStreamState]]', default => sub { [ undef ] }, ); has 'last_marker' => ( is => 'rw', isa => 'Int', default => 0, ); has 'name' => ( is => 'rw', isa => 'Str', default => q{}, ); sub get_line { my ($self) = @_; return $self->line; } sub set_line { my ($self, $value) = @_; $self->line($value); return; } sub get_char_position_in_line { my ($self) = @_; return $self->char_position_in_line; } sub set_char_position_in_line { my ($self, $value) = @_; $self->char_position_in_line($value); return; } sub reset { my ($self) = @_; $self->p(0); $self->line(1); $self->char_position_in_line(0); $self->mark_depth(0); return; } sub consume { my ($self) = @_; if ($self->p < length $self->input) { $self->char_position_in_line($self->char_position_in_line + 1); if (substr($self->input, $self->p, 1) eq "\n") { $self->line($self->line + 1); $self->char_position_in_line(0); } $self->p($self->p + 1); } return; } sub LA { my ($self, $i) = @_; if ($i == 0) { return undef; } if ($i < 0) { ++$i; # e.g., translate LA(-1) to use offset i=0; then input[p+0-1] if ($self->p + $i - 1 < 0) { return $self->EOF; } } if ($self->p + $i - 1 >= length $self->input) { return $self->EOF; } return substr $self->input, $self->p + $i - 1, 1; } sub LT { my ($self, $i) = @_; return $self->LA($i); } sub index { my ($self) = @_; return $self->p; } sub size { my ($self) = @_; return length $self->input; } sub mark { my ($self) = @_; $self->mark_depth($self->mark_depth + 1); my $state; if ($self->mark_depth >= @{$self->markers}) { $state = ANTLR::Runtime::CharStreamState->new(); push @{$self->markers}, $state; } else { $state = $self->markers->[$self->mark_depth]; } $state->set_p($self->p); $state->set_line($self->line); $state->set_char_position_in_line($self->char_position_in_line); $self->last_marker($self->mark_depth); return $self->mark_depth; } sub rewind { my $self = shift; my $m; if (@_ == 0) { $m = $self->last_marker; } else { $m = shift; } my $state = $self->markers->[$m]; # restore stream state $self->seek($state->get_p); $self->line($state->get_line); $self->char_position_in_line($state->get_char_position_in_line); $self->release($m); return; } sub release { my ($self, $marker) = @_; # unwind any other markers made after m and release m $self->mark_depth($marker); # release this marker $self->mark_depth($self->mark_depth - 1); return; } # consume() ahead unit p == index; can't just set p = index as we must update # line and char_position_in_line sub seek { my ($self, $index) = @_; if ($index <= $self->p) { # just jump; don't update stream state (line, ...) $self->p($index); return; } # seek forward, consume until p hits index while ($self->p < $index) { $self->consume(); } return; } sub substring { my ($self, $start, $stop) = @_; return substr $self->input, $start, $stop - $start + 1; } sub get_source_name { my ($self) = @_; return $self->name; } no Moose; __PACKAGE__->meta->make_immutable(); 1;