Language::Befunge::IP - an Instruction Pointer for a Befunge-98 program.


Language-Befunge documentation Contained in the Language-Befunge distribution.

Index


Code Index:

NAME

Top

Language::Befunge::IP - an Instruction Pointer for a Befunge-98 program.

DESCRIPTION

Top

This is the class implementing the Instruction Pointers. An Instruction Pointer (aka IP) has a stack, and a stack of stacks that can be manipulated via the methods of the class.

We need a class, since this is a concurrent Befunge, so we can have more than one IP travelling on the Lahey space.

CONSTRUCTORS

Top

my $ip = LB::IP->new( [$dimensions] )

Create a new Instruction Pointer, which operates in a universe of the given $dimensions. If $dimensions is not specified, it defaults to 2 (befunge world).

my $clone = $ip->clone()

Clone the current Instruction Pointer with all its stacks, position, delta, etc. Change its unique ID.

ACCESSORS

Top

Attributes

The following is a list of attributes of a Language::Befunge::IP object. For each of them, a method get_foobar and set_foobar exists.

$ip->get_id() / $ip->set_id($id)

The unique ID of the IP (an integer). Don't set the ID yourself.

$ip->get_dims()

The number of dimensions this IP operates in (an integer). This is read-only.

$ip->get_position() / $ip->set_position($vec)

The current coordinates of the IP (a Language::Befunge::Vector object).

$ip->get_delta() / $ip->set_delta($vec)

The velocity of the IP (a Language::Befunge::Vector object).

$ip->get_storage() / $ip->set_storage($vec)

The coordinates of the storage offset of the IP (a Language::Befunge::Vector object).

$ip->get_data() / $ip->set_data({})

The library private storage space (a hash reference). Don't set this yourself. FIXME: not supposed to be accessible

$ip->get_string_mode() / set_string_mode($bool)

The string_mode of the IP (a boolean).

$ip->get_end() / $ip->set_end($bool)

Whether the IP should be terminated (a boolean).

$ip->get_libs() / $ip->set_libs($aref)

The current stack of loaded libraries (an array reference). Don't set this yourself. FIXME: not supposed to be accessible

$ip->get_ss() / $ip->set_ss($aref)

The stack of stack of the IP (an array reference). Don't set this yourself. FIXME: not supposed to be accessible

$ip->get_toss() / $ip->set_toss($aref)

The current stack (er, TOSS) of the IP (an array reference). Don't set this yourself. FIXME: not supposed to be accessible

$ip->soss([$])

Get or set the SOSS.

PUBLIC METHODS

Top

Internal stack

In this section, I speak about the stack. In fact, this is the TOSS - that is, the Top Of the Stack Stack.

In Befunge-98, standard stack operations occur transparently on the TOSS (as if there were only one stack, as in Befunge-93).

scount( )

Return the number of elements in the stack.

spush( value )

Push a value on top of the stack.

spush_vec( vector )

Push a vector on top of the stack. The x coordinate is pushed first.

spush_args ( arg, ... )

Push a list of argument on top of the stack (the first argument will be the deeper one). Convert each argument: a number is pushed as is, whereas a string is pushed as a 0gnirts.

/!\ Do not push references or weird arguments: this method supports only numbers (positive and negative) and strings.

spop( )

Pop a value from the stack. If the stack is empty, no error occurs and the method acts as if it popped a 0.

spop_mult( <count> )

Pop multiple values from the stack. If the stack becomes empty, the remainder of the returned values will be 0.

spop_vec( )

Pop a vector from the stack. Returns a Vector object.

spop_gnirts( )

Pop a 0gnirts string from the stack.

sclear( )

Clear the stack.

svalue( offset )

Return the offsetth value of the TOSS, counting from top of the TOSS. The offset is interpreted as a negative value, that is, a call with an offset of 2 or -2 would return the second value on top of the TOSS.

Stack stack

This section discusses about the stack stack. We can speak here about TOSS (Top Of Stack Stack) and SOSS (second on stack stack).

ss_count( )

Return the number of stacks in the stack stack. This of course does not include the TOSS itself.

ss_create( count )

Push the TOSS on the stack stack and create a new stack, aimed to be the new TOSS. Once created, transfer count elements from the SOSS (the former TOSS) to the TOSS. Transfer here means move - and not copy -, furthermore, order is preserved.

If count is negative, then count zeroes are pushed on the new TOSS.

ss_remove( count )

Move count elements from TOSS to SOSS, discard TOSS and make the SOSS become the new TOSS. Order of elems is preserved.

ss_transfer( count )

Transfer count elements from SOSS to TOSS, or from TOSS to SOSS if count is negative; the transfer is done via pop/push.

The order is not preserved, it is reversed.

ss_sizes( )

Return a list with all the sizes of the stacks in the stack stack (including the TOSS), from the TOSS to the BOSS.

soss_count( )

Return the number of elements in SOSS.

soss_push( value )

Push a value on top of the SOSS.

soss_pop_mult( <count> )

Pop multiple values from the SOSS. If the stack becomes empty, the remainder of the returned values will be 0.

soss_push_vec( vector )

Push a vector on top of the SOSS.

soss_pop( )

Pop a value from the SOSS. If the stack is empty, no error occurs and the method acts as if it popped a 0.

soss_pop_vec( )

Pop a vector from the SOSS. If the stack is empty, no error occurs and the method acts as if it popped a 0. returns a Vector.

soss_clear( )

Clear the SOSS.

Changing direction

dir_go_east( )

Implements the > instruction. Force the IP to travel east.

dir_go_west( )

Implements the < instruction. Force the IP to travel west.

dir_go_north( )

Implements the ^ instruction. Force the IP to travel north.

Not valid for Unefunge.

dir_go_south( )

Implements the v instruction. Force the IP to travel south.

Not valid for Unefunge.

dir_go_high( )

Implements the h instruction. Force the IP to travel up.

Not valid for Unefunge or Befunge.

dir_go_low( )

Implements the l instruction. Force the IP to travel down.

Not valid for Unefunge or Befunge.

dir_go_away( )

Implements the ? instruction. Cause the IP to travel in a random cardinal direction (in Befunge's case, one of: north, south, east or west).

dir_turn_left( )

Implements the [ instruction. Rotate by 90 degrees on the left the delta of the IP which encounters this instruction.

Not valid for Unefunge. For Trefunge and greater, only affects the X and Y axes.

dir_turn_right( )

Implements the ] instruction. Rotate by 90 degrees on the right the delta of the IP which encounters this instruction.

Not valid for Unefunge. For Trefunge and higher dimensions, only affects the X and Y axes.

dir_reverse( )

Implements the r instruction. Reverse the direction of the IP, that is, multiply the IP's delta by -1.

Libraries semantics

load( obj )

Load the given library semantics. The parameter is an extension object (a library instance).

unload( lib )

Unload the given library semantics. The parameter is the library name.

Return the library name if it was correctly unloaded, undef otherwise.

/!\ If the library has been loaded twice, this method will only unload the most recent library. Ie, if an IP has loaded the libraries ( FOO, BAR, FOO, BAZ ) and one calls unload( "FOO" ), then the IP will follow the semantics of BAZ, then BAR, then <FOO> (!).

extdata( library, [value] )

Store or fetch a value in a private space. This private space is reserved for libraries that need to store internal values.

Since in Perl references are plain scalars, one can store a reference to an array or even a hash.

SEE ALSO

Top

Language::Befunge.

AUTHOR

Top

Jerome Quelin, <jquelin@cpan.org>

COPYRIGHT & LICENSE

Top


Language-Befunge documentation Contained in the Language-Befunge distribution.

#
# This file is part of Language::Befunge.
# Copyright (c) 2001-2009 Jerome Quelin, all rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the same terms as Perl itself.
#
#

package Language::Befunge::IP;
require 5.010;

use strict;
use warnings;
use integer;

use Carp;
use Language::Befunge::Vector;
use Storable qw(dclone);

use Class::XSAccessor
    getters => {
        get_position    => 'position',
        get_data        => 'data',
        get_delta       => 'delta',
        get_dims        => 'dims',
        get_end         => 'end',
        get_id          => 'id',
        get_libs        => 'libs',
        get_ss          => 'ss',
        get_storage     => 'storage',
        get_string_mode => 'string_mode',
        get_toss        => 'toss',
    },
    setters => {
        set_position    => 'position',
        set_data        => 'data',
        set_delta       => 'delta',
        set_end         => 'end',
        set_id          => 'id',
        set_libs        => 'libs',
        set_ss          => 'ss',
        set_storage     => 'storage',
        set_string_mode => 'string_mode',
        set_toss        => 'toss',
    };


# -- CONSTRUCTORS

sub new {
    my ($class, $dims) = @_;
    $dims = 2 unless defined $dims;
    my $self  =
      { id           => 0,
        dims         => $dims,
        toss         => [],
        ss           => [],
        position     => Language::Befunge::Vector->new_zeroes($dims),
        delta        => Language::Befunge::Vector->new_zeroes($dims),
        storage      => Language::Befunge::Vector->new_zeroes($dims),
        string_mode  => 0,
        end          => 0,
        data         => {},
        libs         => { map { $_=>[] } 'A'..'Z' },
      };
    # go right by default
    $self->{delta}->set_component(0, 1);
    bless $self, $class;
    $self->set_id( $self->_get_new_id );
    return $self;
}

sub clone {
    my $self = shift;
    my $clone = dclone( $self );
    $clone->set_id( $self->_get_new_id );
    return $clone;
}


# -- ACCESSORS


sub soss {
    my $self = shift;
    # Remember, the Stack Stack is up->bottom.
    @_ and $self->get_ss->[0] = shift;
    return $self->get_ss->[0];
}


sub scount {
    my $self = shift;
    return scalar @{ $self->get_toss };
}

sub spush {
    my $self = shift;
    push @{ $self->get_toss }, @_;
}

sub spush_vec {
    my ($self) = shift;
    foreach my $v (@_) {
        $self->spush($v->get_all_components);
    }
}

sub spush_args {
    my $self = shift;
    foreach my $arg ( @_ ) {
        $self->spush
          ( ($arg =~ /^-?\d+$/) ?
              $arg                                    # A number.
            : reverse map {ord} split //, $arg.chr(0) # A string.
          );
    }
}

sub spop {
    my $self = shift;
    my $val = pop @{ $self->get_toss };
    defined $val or $val = 0;
    return $val;
}

sub spop_mult {
    my ($self, $count) = @_;
    my @rv = reverse map { $self->spop() } (1..$count);
    return @rv;
}

sub spop_vec {
    my $self = shift;
    return Language::Befunge::Vector->new($self->spop_mult($self->get_dims));
}

sub spop_gnirts {
    my $self = shift;
    my ($val, $str);
    do {
        $val = pop @{ $self->get_toss };
        defined $val or $val = 0;
        $str .= chr($val);
    } while( $val != 0 );
    chop $str; # Remove trailing \0.
    return $str;
}

sub sclear {
    my $self = shift;
    $self->set_toss( [] );
}

sub svalue {
    my ($self, $idx) = @_;

    $idx = - abs( $idx );
    return 0 unless exists $self->get_toss->[$idx];
    return $self->get_toss->[$idx];
}

sub ss_count {
    my $self = shift;
    return scalar( @{ $self->get_ss } );
}

sub ss_create {
    my ( $self, $n ) = @_;

    my @new_toss;

    if ( $n < 0 ) {
        # Push zeroes on *current* toss (to-be soss).
        $self->spush( (0) x abs($n) );
    } elsif ( $n > 0 ) {
        my $c = $n - $self->scount;
        if ( $c <= 0 ) {
            # Transfer elements.
            @new_toss = splice @{ $self->get_toss }, -$n;
        } else {
            # Transfer elems and fill with zeroes.
            @new_toss = ( (0) x $c, @{ $self->get_toss } );
            $self->sclear;
        }
    }
    # $n == 0: do nothing


    # Push the former TOSS on the stack stack and copy reference to
    # the new TOSS.
    # For commodity reasons, the Stack Stack is oriented up->bottom
    # (that is, a push is an unshift, and a pop is a shift).
    unshift @{ $self->get_ss }, $self->get_toss;
    $self->set_toss( \@new_toss );
}

sub ss_remove {
    my ( $self, $n ) = @_;

    # Fetch the TOSS.
    # Remember, the Stack Stack is up->bottom.
    my $new_toss = shift @{ $self->get_ss };

    if ( $n < 0 ) {
        # Remove values.
        if ( scalar(@$new_toss) >= abs($n) ) {
            splice @$new_toss, $n;
        } else {
            $new_toss = [];
        }
    } elsif ( $n > 0 ) {
        my $c = $n - $self->scount;
        if ( $c <= 0 ) {
            # Transfer elements.
            push @$new_toss, splice( @{ $self->get_toss }, -$n );
        } else {
            # Transfer elems and fill with zeroes.
            push @$new_toss, ( (0) x $c, @{ $self->get_toss } );
        }
    }
    # $n == 0: do nothing


    # Store the new TOSS.
    $self->set_toss( $new_toss );
}

sub ss_transfer {
    my ($self, $n) = @_;
    $n == 0 and return;

    if ( $n > 0 ) {
        # Transfer from SOSS to TOSS.
        my $c = $n - $self->soss_count;
        my @elems;
        if ( $c <= 0 ) {
            @elems = splice @{ $self->soss }, -$n;
        } else {
            @elems = ( (0) x $c, @{ $self->soss } );
            $self->soss_clear;
        }
        $self->spush( reverse @elems );

    } else {
        $n = -$n;
        # Transfer from TOSS to SOSS.
        my $c = $n - $self->scount;
        my @elems;
        if ( $c <= 0 ) {
            @elems = splice @{ $self->get_toss }, -$n;
        } else {
            @elems = ( (0) x $c, @{ $self->get_toss } );
            $self->sclear;
        }
        $self->soss_push( reverse @elems );

    }
}

sub ss_sizes {
    my $self = shift;

    my @sizes = ( $self->scount );

    # Store the size of each stack.
    foreach my $i ( 1..$self->ss_count ) {
        push @sizes, scalar @{ $self->get_ss->[$i-1] };
    }

    return @sizes;
}


sub soss_count {
    my $self = shift;
    return scalar( @{ $self->soss } );
}

sub soss_push {
    my $self = shift;
    push @{ $self->soss }, @_;
}


sub soss_pop_mult {
    my ($self, $count) = @_;
    my @rv = reverse map { $self->soss_pop() } (1..$count);
    return @rv;
}

sub soss_push_vec {
    my $self = shift;
    foreach my $v (@_) {
        $self->soss_push($v->get_all_components);
    }
}

sub soss_pop {
    my $self = shift;
    my $val = pop @{ $self->soss };
    defined $val or $val = 0;
    return $val;
}

sub soss_pop_vec {
    my $self = shift;
    return Language::Befunge::Vector->new($self->soss_pop_mult($self->get_dims));
}

sub soss_clear {
    my $self = shift;
    $self->soss( [] );
}



sub dir_go_east {
    my $self = shift;
    $self->get_delta->clear;
    $self->get_delta->set_component(0, 1);
}

sub dir_go_west {
    my $self = shift;
    $self->get_delta->clear;
    $self->get_delta->set_component(0, -1);
}

sub dir_go_north {
    my $self = shift;
    $self->get_delta->clear;
    $self->get_delta->set_component(1, -1);
}

sub dir_go_south {
    my $self = shift;
    $self->get_delta->clear;
    $self->get_delta->set_component(1, 1);
}

sub dir_go_high {
    my $self = shift;
    $self->get_delta->clear;
    $self->get_delta->set_component(2, 1);
}

sub dir_go_low {
    my $self = shift;
    $self->get_delta->clear;
    $self->get_delta->set_component(2, -1);
}

sub dir_go_away {
    my $self = shift;
    my $nd = $self->get_dims;
    my $dim = (0..$nd-1)[int(rand $nd)];
    $self->get_delta->clear;
    my $value = (-1, 1)[int(rand 2)];
    $self->get_delta->set_component($dim, $value);
}

sub dir_turn_left {
    my $self = shift;
    my $old_dx = $self->get_delta->get_component(0);
    my $old_dy = $self->get_delta->get_component(1);
    $self->get_delta->set_component(0, 0 + $old_dy);
    $self->get_delta->set_component(1, 0 + $old_dx * -1);
}

sub dir_turn_right {
    my $self = shift;
    my $old_dx = $self->get_delta->get_component(0);
    my $old_dy = $self->get_delta->get_component(1);
    $self->get_delta->set_component(0, 0 + $old_dy * -1);
    $self->get_delta->set_component(1, 0 + $old_dx);
}

sub dir_reverse {
    my $self = shift;
    $self->set_delta(-$self->get_delta);
}

sub load {
    my ($self, $lib) = @_;

    my $libs = $self->get_libs;
    foreach my $letter ( 'A' .. 'Z' ) {
        next unless $lib->can($letter);
        push @{ $libs->{$letter} }, $lib;
    }
}

sub unload {
    my ($self, $lib) = @_;

    my $libs = $self->get_libs;
    foreach my $letter ( 'A' .. 'Z' ) {
        next unless $lib->can($letter);
        pop @{ $libs->{$letter} };
    }
}

sub extdata {
    my $self = shift;
    my $lib  = shift;
    @_ ? $self->get_data->{$lib} = shift : $self->get_data->{$lib};
}


# -- PRIVATE METHODS

#
# my $id = _get_new_id;
#
# Forge a new IP id, that will distinct it from the other IPs of the program.
#
my $id = 0;
sub _get_new_id {
    return $id++;
}

1;
__END__