tagged release 0.6.4
[parrot.git] / languages / jako / lib / Jako / Processor.pm
bloba418a2d174e801d45b45366bc7bb71b67ffabc10
2 # Processor.pm
4 # Copyright (C) 2002-2005, The Perl Foundation.
5 # This program is free software. It is subject to the same license
6 # as the Parrot interpreter.
8 # $Id$
11 use strict;
12 use warnings;
14 package Jako::Processor;
16 use Carp;
19 # new()
22 sub new {
23 confess "Subclass responsibility";
27 # debug()
30 sub debug {
31 my $self = shift;
33 if (@_) {
34 $self->{DEBUG} = shift;
37 return $self->{DEBUG};
41 # file()
44 sub file {
45 my $self = shift;
47 if (@_) { $self->{FILE} = shift; }
49 return defined $self->{FILE} ? $self->{FILE} : '<NO FILE>';
53 # line()
56 sub line {
57 my $self = shift;
59 if (@_) { $self->{LINE} = shift; }
61 return defined $self->{LINE} ? $self->{LINE} : '';
64 ###############################################################################
65 ###############################################################################
67 ## TOKEN PROCESSING:
69 ###############################################################################
70 ###############################################################################
73 # tokens()
76 sub tokens {
77 my $self = shift;
79 if (@_) {
80 $self->{TOKENS} = [@_];
82 else {
83 return @{ $self->{TOKENS} };
88 # token_count()
90 # Returns the number of tokens.
93 sub count {
94 my $self = shift;
96 return scalar( @{ $self->{TOKENS} } );
100 # pos()
102 # Returns (or sets) the abolute position.
105 sub pos {
106 my $self = shift;
107 my ($pos) = @_;
109 if ( defined $pos ) {
110 my $count = scalar( @{ $self->{TOKENS} } );
112 $pos = -1 if $pos < 0; # Just before the beginning
113 $pos = $count if $pos > $count; # Just past the end (in case we get more tokens)
115 $self->{POS} = $pos;
117 $self->file( $self->at($pos)->file );
118 $self->line( $self->at($pos)->line );
121 return defined $self->{POS} ? $self->{POS} : -1;
125 # at()
127 # Without an argument, gives the current token. With an argument, gives the
128 # token at that absolute position. If a count is given, then that many tokens
129 # (at most, given the possibilitiy of running off the end) are returned. If
130 # a count is not given, 1 is inferred.
133 sub at {
134 my $self = shift;
135 my ( $index, $count ) = @_;
137 $index = $self->pos unless defined $index;
139 $count = 1 unless defined $count;
141 $self->INTERNAL_ERROR( "Attempt to get fewer than 1 token (%s)!", $count )
142 if $count < 1;
144 if ( $count > 1 ) {
145 return map { $self->at( $index + $_ ) } ( 0 .. $count );
148 my $pos = $self->pos;
150 $self->INTERNAL_ERROR("Position is not defined") unless defined $pos;
152 return Jako::Token->BOF if $index < 0;
153 return Jako::Token->EOF if $index >= @{ $self->{TOKENS} };
155 return $self->{TOKENS}[$index];
159 # get()
161 # Without an argument, gives the current token. With an argument, gives the
162 # token at that offset from the current one. If a count is given, then that
163 # many tokens (at most, given the possibility of running of the end) are
164 # returned. If a count is not given, 1 is inferred.
167 sub get {
168 my $self = shift;
169 my ( $offset, $count ) = @_;
171 $offset = 0 unless defined $offset;
173 my $pos = $self->pos;
175 $self->INTERNAL_ERROR("Position is not defined") unless defined $pos;
176 $self->INTERNAL_ERROR("Offset is not defined") unless defined $offset;
178 # DEBUG(0, "Current position is $pos");
179 $pos += $offset;
181 # DEBUG(0, "Getting token at $pos");
183 return $self->at( $pos, $count );
187 # forth()
189 # Moves the current token position $offset tokens forward. Returns the
190 # token at the new absolute position.
193 sub forth {
194 my $self = shift;
195 my ($offset) = @_;
197 $offset = 1 unless defined $offset;
199 my $pos = $self->pos;
201 $self->INTERNAL_ERROR("Position is undefined") unless defined $pos;
203 # DEBUG(0, "Position starts as $pos");
205 $pos += $offset;
207 # DEBUG(0, "Setting position to $pos");
208 $self->pos($pos);
210 # DEBUG(0, "forth(): Position is now %d", $self->pos);
212 my $token = $self->get;
214 $self->file( $token->file );
215 $self->line( $token->line );
217 return $token;
221 # back()
223 # Moves the current token position $offset tokens backward. Returns the new
224 # absolute position.
227 sub back {
228 my $self = shift;
229 my ($offset) = @_;
231 $offset = 1 unless defined $offset;
233 return $self->forth( -$offset );
237 # dump()
239 # Dump the tokens.
242 sub dump {
243 my $self = shift;
245 while (1) {
246 my $tok = $self->forth;
248 printf STDERR "%6d : %-30s : %5s : %-15s: %1s : %s\n", $self->pos, $tok->file, $tok->line,
249 $tok->kind, $tok->type || '', $tok->text;
251 last if $tok->is_eof;
254 =begin commented_out
256 foreach (my $i = 0; $i < scalar(@{$self->{TOKENS}}); $i++) {
257 my $tok = $self->at($i);
259 printf STDERR "%6d : %-30s : %5s : %-15s: %1s : %s\n",
261 $tok->file,
262 $tok->line,
263 $tok->kind,
264 $tok->type || '',
265 $tok->text;
267 last if $tok->is_eof;
270 =cut
275 # require()
277 # Require consumes and returns the token if the requirement is met, otherwise
278 # a Syntax Error is raised.
280 # If more than one type is specified, the semantics are "or".
283 sub require {
284 my $self = shift;
286 # DEBUG(0, "Requiring '$kind'");
288 $self->SYNTAX_ERROR(
289 "Expected %s, but found '%s'.",
290 @_ > 1 ? "one of (" . join( ", ", @_ ) . ")" : $_[0],
291 $self->get(1)->text
292 ) unless grep { $self->get(1)->kind eq $_ } @_;
294 return $self->forth;
298 # REQUIRES:
301 sub require_assign { return shift->require('assign'); }
302 sub require_close_brace { return shift->require('close-brace'); }
303 sub require_close_bracket { return shift->require('close-bracket'); }
304 sub require_close_paren { return shift->require('close-paren'); }
305 sub require_comma { return shift->require('comma'); }
306 sub require_ident { return shift->require('ident'); }
307 sub require_literal { return shift->require('literal'); }
308 sub require_open_brace { return shift->require('open-brace'); }
309 sub require_open_bracket { return shift->require('open-bracket'); }
310 sub require_open_paren { return shift->require('open-paren'); }
311 sub require_infix_rel { return shift->require('infix-rel'); }
312 sub require_semicolon { return shift->require('semicolon'); }
313 sub require_type { return shift->require('type'); }
315 sub require_value { return shift->require( 'ident', 'literal' ); }
318 # skip()
320 # Skips the token type specified if it is present. Returns undef if nothing
321 # was skipped, otherwise returns the token skipped.
323 # If more than one type is specified, the semantics are "or".
326 sub skip {
327 my $self = shift;
328 my ($kind) = @_;
330 return undef unless grep { $self->get(1)->kind eq $_ } @_;
332 return $self->forth;
336 # SKIPS:
339 sub skip_assign { return shift->skip('assign'); }
340 sub skip_colon { return shift->skip('colon'); }
341 sub skip_comma { return shift->skip('comma'); }
342 sub skip_close_brace { return shift->skip('close-brace'); }
343 sub skip_close_bracket { return shift->skip('close-bracket'); }
344 sub skip_close_paren { return shift->skip('close-paren'); }
345 sub skip_ident { return shift->skip('ident'); }
346 sub skip_literal { return shift->skip('literal'); }
347 sub skip_new { return shift->skip('new'); }
348 sub skip_open_brace { return shift->skip('open-brace'); }
349 sub skip_open_bracket { return shift->skip('open-bracket'); }
350 sub skip_open_paren { return shift->skip('open-paren'); }
351 sub skip_infix_rel { return shift->skip('infix-rel'); }
352 sub skip_semicolon { return shift->skip('semicolon'); }
353 sub skip_type { return shift->skip('type'); }
355 sub skip_value { return shift->skip( 'ident', 'literal' ); }
357 ###############################################################################
358 ###############################################################################
360 ## MESSAGES:
362 ###############################################################################
363 ###############################################################################
366 # DEBUG()
369 sub DEBUG {
370 my ( $self, $level, $format, @args ) = @_;
372 return unless defined $self->debug and $self->debug >= $level;
374 my $message = $format ? sprintf( $format, @args ) : '<no message>';
376 printf STDERR "%s [%s]: Debug message: %s\n", $self->file, $self->line, $message;
380 # WARNING()
383 sub WARNING {
384 my ( $self, $format, @args ) = @_;
386 my $message = sprintf( $format, @args );
388 print STDERR "%s [%s]: Warning: %s", $self->file, $self->line, $message;
392 # ERROR()
395 sub ERROR {
396 my ( $self, $kind, $format, @args ) = @_;
398 my $message = sprintf( $format, @args );
400 $message = sprintf( "%s [%s]: %s error: %s", $self->file, $self->line, $kind, $message );
402 if ( $self->debug ) { confess $message; }
403 else { die "$message\n"; }
407 # PARSE_ERROR()
410 sub PARSE_ERROR {
411 my ( $self, $format, @args ) = @_;
412 $self->ERROR( 'Parse', $format, @args );
416 # SYNTAX_ERROR()
419 sub SYNTAX_ERROR {
420 my ( $self, $format, @args ) = @_;
421 $self->ERROR( 'Syntax', $format, @args );
425 # INTERNAL_ERROR()
428 sub INTERNAL_ERROR {
429 my ( $self, $format, @args ) = @_;
430 $self->ERROR( 'Internal', $format, @args );
436 # Local Variables:
437 # mode: cperl
438 # cperl-indent-level: 4
439 # fill-column: 100
440 # End:
441 # vim: expandtab shiftwidth=4: