2 # This file is part of Language::Befunge.
3 # Copyright (c) 2001-2008 Jerome Quelin, all rights reserved.
5 # This program is free software; you can redistribute it and/or modify
6 # it under the same terms as Perl itself.
10 package Language
::Befunge
::IP
;
18 use Language
::Befunge
::Vector
;
19 use Storable
qw(dclone);
25 my ($class, $dims) = @_;
26 $dims = 2 unless defined $dims;
32 position
=> Language
::Befunge
::Vector
->new_zeroes($dims),
33 delta
=> Language
::Befunge
::Vector
->new_zeroes($dims),
34 storage
=> Language
::Befunge
::Vector
->new_zeroes($dims),
38 libs
=> { map { $_=>[] } 'A'..'Z' },
41 $self->{delta
}->set_component(0, 1);
43 $self->set_id( $self->_get_new_id );
49 my $clone = dclone
( $self );
50 $clone->set_id( $self->_get_new_id );
58 my @attrs = qw
[ position data delta end id libs
59 ss storage string_mode toss
];
60 foreach my $attr ( @attrs ) {
61 my $code = qq[ sub get_
$attr { return \
$_[0]->{$attr} } ];
62 $code .= qq[ sub set_
$attr { \
$_[0]->{$attr} = \
$_[1] } ];
67 sub get_dims
{ return $_[0]->{dims
} };
71 # Remember, the Stack Stack is up->bottom.
72 @_ and $self->get_ss->[0] = shift;
73 return $self->get_ss->[0];
79 return scalar @
{ $self->get_toss };
84 push @
{ $self->get_toss }, @_;
90 $self->spush($v->get_all_components);
96 foreach my $arg ( @_ ) {
98 ( ($arg =~ /^-?\d+$/) ?
100 : reverse map {ord} split //, $arg.chr(0) # A string.
107 my $val = pop @
{ $self->get_toss };
108 defined $val or $val = 0;
113 my ($self, $count) = @_;
114 my @rv = reverse map { $self->spop() } (1..$count);
120 return Language
::Befunge
::Vector
->new($self->spop_mult($self->get_dims));
127 $val = pop @
{ $self->get_toss };
128 defined $val or $val = 0;
130 } while( $val != 0 );
131 chop $str; # Remove trailing \0.
137 $self->set_toss( [] );
141 my ($self, $idx) = @_;
143 $idx = - abs( $idx );
144 return 0 unless exists $self->get_toss->[$idx];
145 return $self->get_toss->[$idx];
150 return scalar( @
{ $self->get_ss } );
154 my ( $self, $n ) = @_;
159 # Push zeroes on *current* toss (to-be soss).
160 $self->spush( (0) x
abs($n) );
162 my $c = $n - $self->scount;
165 @new_toss = splice @
{ $self->get_toss }, -$n;
167 # Transfer elems and fill with zeroes.
168 @new_toss = ( (0) x
$c, @
{ $self->get_toss } );
172 # $n == 0: do nothing
175 # Push the former TOSS on the stack stack and copy reference to
177 # For commodity reasons, the Stack Stack is oriented up->bottom
178 # (that is, a push is an unshift, and a pop is a shift).
179 unshift @
{ $self->get_ss }, $self->get_toss;
180 $self->set_toss( \
@new_toss );
184 my ( $self, $n ) = @_;
187 # Remember, the Stack Stack is up->bottom.
188 my $new_toss = shift @
{ $self->get_ss };
192 if ( scalar(@
$new_toss) >= abs($n) ) {
193 splice @
$new_toss, $n;
198 my $c = $n - $self->scount;
201 push @
$new_toss, splice( @
{ $self->get_toss }, -$n );
203 # Transfer elems and fill with zeroes.
204 push @
$new_toss, ( (0) x
$c, @
{ $self->get_toss } );
207 # $n == 0: do nothing
210 # Store the new TOSS.
211 $self->set_toss( $new_toss );
219 # Transfer from SOSS to TOSS.
220 my $c = $n - $self->soss_count;
223 @elems = splice @
{ $self->soss }, -$n;
225 @elems = ( (0) x
$c, @
{ $self->soss } );
228 $self->spush( reverse @elems );
232 # Transfer from TOSS to SOSS.
233 my $c = $n - $self->scount;
236 @elems = splice @
{ $self->get_toss }, -$n;
238 @elems = ( (0) x
$c, @
{ $self->get_toss } );
241 $self->soss_push( reverse @elems );
249 my @sizes = ( $self->scount );
251 # Store the size of each stack.
252 foreach my $i ( 1..$self->ss_count ) {
253 push @sizes, scalar @
{ $self->get_ss->[$i-1] };
262 return scalar( @
{ $self->soss } );
267 push @
{ $self->soss }, @_;
272 my ($self, $count) = @_;
273 my @rv = reverse map { $self->soss_pop() } (1..$count);
280 $self->soss_push($v->get_all_components);
286 my $val = pop @
{ $self->soss };
287 defined $val or $val = 0;
293 return Language
::Befunge
::Vector
->new($self->soss_pop_mult($self->get_dims));
305 $self->get_delta->clear;
306 $self->get_delta->set_component(0, 1);
311 $self->get_delta->clear;
312 $self->get_delta->set_component(0, -1);
317 $self->get_delta->clear;
318 $self->get_delta->set_component(1, -1);
323 $self->get_delta->clear;
324 $self->get_delta->set_component(1, 1);
329 $self->get_delta->clear;
330 $self->get_delta->set_component(2, 1);
335 $self->get_delta->clear;
336 $self->get_delta->set_component(2, -1);
341 my $nd = $self->get_dims;
342 my $dim = (0..$nd-1)[int(rand $nd)];
343 $self->get_delta->clear;
344 my $value = (-1, 1)[int(rand 2)];
345 $self->get_delta->set_component($dim, $value);
350 my $old_dx = $self->get_delta->get_component(0);
351 my $old_dy = $self->get_delta->get_component(1);
352 $self->get_delta->set_component(0, 0 + $old_dy);
353 $self->get_delta->set_component(1, 0 + $old_dx * -1);
358 my $old_dx = $self->get_delta->get_component(0);
359 my $old_dy = $self->get_delta->get_component(1);
360 $self->get_delta->set_component(0, 0 + $old_dy * -1);
361 $self->get_delta->set_component(1, 0 + $old_dx);
366 $self->set_delta(-$self->get_delta);
370 my ($self, $lib) = @_;
372 my $libs = $self->get_libs;
373 foreach my $letter ( 'A' .. 'Z' ) {
374 next unless $lib->can($letter);
375 push @
{ $libs->{$letter} }, $lib;
380 my ($self, $lib) = @_;
382 my $libs = $self->get_libs;
383 foreach my $letter ( 'A' .. 'Z' ) {
384 next unless $lib->can($letter);
385 pop @
{ $libs->{$letter} };
392 @_ ?
$self->get_data->{$lib} = shift : $self->get_data->{$lib};
399 # my $id = _get_new_id;
401 # Forge a new IP id, that will distinct it from the other IPs of the program.
413 Language::Befunge::IP - an Instruction Pointer for a Befunge-98 program.
419 This is the class implementing the Instruction Pointers. An
420 Instruction Pointer (aka IP) has a stack, and a stack of stacks that
421 can be manipulated via the methods of the class.
423 We need a class, since this is a concurrent Befunge, so we can have
424 more than one IP travelling on the Lahey space.
430 =head2 my $ip = LB::IP->new( [$dimensions] )
432 Create a new Instruction Pointer, which operates in a universe of the given
433 C<$dimensions>. If C<$dimensions> is not specified, it defaults to 2
437 =head2 my $clone = $ip->clone()
439 Clone the current Instruction Pointer with all its stacks, position,
440 delta, etc. Change its unique ID.
448 The following is a list of attributes of a Language::Befunge::IP
449 object. For each of them, a method C<get_foobar> and C<set_foobar>
455 =item $ip->get_id() / $ip->set_id($id)
457 The unique ID of the IP (an integer). Don't set the ID yourself.
460 =item $ip->get_dims()
462 The number of dimensions this IP operates in (an integer). This is
466 =item $ip->get_position() / $ip->set_position($vec)
468 The current coordinates of the IP (a C<Language::Befunge::Vector>
472 =item $ip->get_delta() / $ip->set_delta($vec)
474 The velocity of the IP (a C<Language::Befunge::Vector> object).
477 =item $ip->get_storage() / $ip->set_storage($vec)
479 The coordinates of the storage offset of the IP (a
480 C<Language::Befunge::Vector> object).
483 =item $ip->get_data() / $ip->set_data({})
485 The library private storage space (a hash reference). Don't set this
487 FIXME: not supposed to be accessible
490 =item $ip->get_string_mode() / set_string_mode($bool)
492 The string_mode of the IP (a boolean).
495 =item $ip->get_end() / $ip->set_end($bool)
497 Whether the IP should be terminated (a boolean).
500 =item $ip->get_libs() / $ip->set_libs($aref)
502 The current stack of loaded libraries (an array reference). Don't set
504 FIXME: not supposed to be accessible
507 =item $ip->get_ss() / $ip->set_ss($aref)
509 The stack of stack of the IP (an array reference). Don't set this
511 FIXME: not supposed to be accessible
514 =item $ip->get_toss() / $ip->set_toss($aref)
516 The current stack (er, TOSS) of the IP (an array reference). Don't set
518 FIXME: not supposed to be accessible
524 =head2 $ip->soss([$])
530 =head1 PUBLIC METHODS
532 =head2 Internal stack
534 In this section, I speak about the stack. In fact, this is the TOSS - that
535 is, the Top Of the Stack Stack.
537 In Befunge-98, standard stack operations occur transparently on the
538 TOSS (as if there were only one stack, as in Befunge-93).
545 Return the number of elements in the stack.
550 Push a value on top of the stack.
553 =item spush_vec( vector )
555 Push a vector on top of the stack. The x coordinate is pushed first.
558 =item spush_args ( arg, ... )
560 Push a list of argument on top of the stack (the first argument will
561 be the deeper one). Convert each argument: a number is pushed as is,
562 whereas a string is pushed as a 0gnirts.
564 B</!\> Do B<not> push references or weird arguments: this method
565 supports only numbers (positive and negative) and strings.
570 Pop a value from the stack. If the stack is empty, no error occurs and
571 the method acts as if it popped a 0.
574 =item spop_mult( <count> )
576 Pop multiple values from the stack. If the stack becomes empty, the
577 remainder of the returned values will be 0.
582 Pop a vector from the stack. Returns a Vector object.
587 Pop a 0gnirts string from the stack.
595 =item svalue( offset )
597 Return the C<offset>th value of the TOSS, counting from top of the
598 TOSS. The offset is interpreted as a negative value, that is, a call
599 with an offset of C<2> or C<-2> would return the second value on top
608 This section discusses about the stack stack. We can speak here about
609 TOSS (Top Of Stack Stack) and SOSS (second on stack stack).
615 Return the number of stacks in the stack stack. This of course does
616 not include the TOSS itself.
620 =item ss_create( count )
622 Push the TOSS on the stack stack and create a new stack, aimed to be
623 the new TOSS. Once created, transfer C<count> elements from the SOSS
624 (the former TOSS) to the TOSS. Transfer here means move - and B<not>
625 copy -, furthermore, order is preserved.
627 If count is negative, then C<count> zeroes are pushed on the new TOSS.
630 =item ss_remove( count )
632 Move C<count> elements from TOSS to SOSS, discard TOSS and make the
633 SOSS become the new TOSS. Order of elems is preserved.
636 =item ss_transfer( count )
638 Transfer C<count> elements from SOSS to TOSS, or from TOSS to SOSS if
639 C<count> is negative; the transfer is done via pop/push.
641 The order is not preserved, it is B<reversed>.
646 Return a list with all the sizes of the stacks in the stack stack
647 (including the TOSS), from the TOSS to the BOSS.
652 Return the number of elements in SOSS.
655 =item soss_push( value )
657 Push a value on top of the SOSS.
660 =item soss_pop_mult( <count> )
662 Pop multiple values from the SOSS. If the stack becomes empty, the
663 remainder of the returned values will be 0.
666 =item soss_push_vec( vector )
668 Push a vector on top of the SOSS.
674 Pop a value from the SOSS. If the stack is empty, no error occurs and
675 the method acts as if it popped a 0.
678 =item soss_pop_vec( )
680 Pop a vector from the SOSS. If the stack is empty, no error occurs
681 and the method acts as if it popped a 0. returns a Vector.
692 =head2 Changing direction
698 Implements the C<E<gt>> instruction. Force the IP to travel east.
703 Implements the C<E<lt>> instruction. Force the IP to travel west.
706 =item dir_go_north( )
708 Implements the C<^> instruction. Force the IP to travel north.
710 Not valid for Unefunge.
713 =item dir_go_south( )
715 Implements the C<v> instruction. Force the IP to travel south.
717 Not valid for Unefunge.
722 Implements the C<h> instruction. Force the IP to travel up.
724 Not valid for Unefunge or Befunge.
729 Implements the C<l> instruction. Force the IP to travel down.
731 Not valid for Unefunge or Befunge.
736 Implements the C<?> instruction. Cause the IP to travel in a random
737 cardinal direction (in Befunge's case, one of: north, south, east or
741 =item dir_turn_left( )
743 Implements the C<[> instruction. Rotate by 90 degrees on the left the
744 delta of the IP which encounters this instruction.
746 Not valid for Unefunge. For Trefunge and greater, only affects the
750 =item dir_turn_right( )
752 Implements the C<]> instruction. Rotate by 90 degrees on the right the
753 delta of the IP which encounters this instruction.
755 Not valid for Unefunge. For Trefunge and higher dimensions, only
756 affects the X and Y axes.
762 Implements the C<r> instruction. Reverse the direction of the IP, that
763 is, multiply the IP's delta by -1.
769 =head2 Libraries semantics
775 Load the given library semantics. The parameter is an extension object
776 (a library instance).
782 Unload the given library semantics. The parameter is the library name.
784 Return the library name if it was correctly unloaded, undef otherwise.
786 B</!\> If the library has been loaded twice, this method will only
787 unload the most recent library. Ie, if an IP has loaded the libraries
788 ( C<FOO>, C<BAR>, C<FOO>, C<BAZ> ) and one calls C<unload( "FOO" )>,
789 then the IP will follow the semantics of C<BAZ>, then C<BAR>, then
793 =item extdata( library, [value] )
795 Store or fetch a value in a private space. This private space is
796 reserved for libraries that need to store internal values.
798 Since in Perl references are plain scalars, one can store a reference
799 to an array or even a hash.
808 L<Language::Befunge>.
813 Jerome Quelin, E<lt>jquelin@cpan.orgE<gt>
817 =head1 COPYRIGHT & LICENSE
819 Copyright (c) 2001-2008 Jerome Quelin, all rights reserved.
821 This program is free software; you can redistribute it and/or modify
822 it under the same terms as Perl itself.