File Coverage

blib/lib/Language/Befunge/IP.pm
Criterion Covered Total %
statement 210 210 100.0
branch 42 42 100.0
condition n/a
subroutine 47 47 100.0
pod 38 38 100.0
total 337 337 100.0


line stmt bran cond sub pod time code
1             #
2             # This file is part of Language-Befunge
3             #
4             # This software is copyright (c) 2003 by Jerome Quelin.
5             #
6             # This is free software; you can redistribute it and/or modify it under
7             # the same terms as the Perl 5 programming language system itself.
8             #
9 73     73   26640 use 5.010;
  73         155  
10 73     73   226 use strict;
  73         78  
  73         1162  
11 73     73   213 use warnings;
  73         76  
  73         2852  
12              
13             package Language::Befunge::IP;
14             # ABSTRACT: an Instruction Pointer for a Befunge-98 program
15             $Language::Befunge::IP::VERSION = '5.000';
16 73     73   30885 use integer;
  73         596  
  73         267  
17              
18 73     73   1630 use Carp;
  73         76  
  73         3043  
19 73     73   22778 use Language::Befunge::Vector;
  73         105  
  73         2096  
20 73     73   40689 use Storable qw(dclone);
  73         199687  
  73         10848  
21              
22             use Class::XSAccessor
23 73         2804 getters => {
24             get_position => 'position',
25             get_data => 'data',
26             get_delta => 'delta',
27             get_dims => 'dims',
28             get_end => 'end',
29             get_id => 'id',
30             get_libs => 'libs',
31             get_ss => 'ss',
32             get_storage => 'storage',
33             get_string_mode => 'string_mode',
34             get_toss => 'toss',
35             },
36             setters => {
37             set_position => 'position',
38             set_data => 'data',
39             set_delta => 'delta',
40             set_end => 'end',
41             set_id => 'id',
42             set_libs => 'libs',
43             set_ss => 'ss',
44             set_storage => 'storage',
45             set_string_mode => 'string_mode',
46             set_toss => 'toss',
47 73     73   34998 };
  73         133755  
48              
49              
50             # -- CONSTRUCTORS
51              
52             sub new {
53 499     499 1 2464 my ($class, $dims) = @_;
54 499 100       852 $dims = 2 unless defined $dims;
55             my $self =
56             { id => 0,
57             dims => $dims,
58             toss => [],
59             ss => [],
60             position => Language::Befunge::Vector->new_zeroes($dims),
61             delta => Language::Befunge::Vector->new_zeroes($dims),
62             storage => Language::Befunge::Vector->new_zeroes($dims),
63             string_mode => 0,
64             end => 0,
65             data => {},
66 499         1671 libs => { map { $_=>[] } 'A'..'Z' },
  12974         18090  
67             };
68             # go right by default
69 499         1989 $self->{delta}->set_component(0, 1);
70 499         469 bless $self, $class;
71 499         888 $self->set_id( $self->_get_new_id );
72 499         982 return $self;
73             }
74              
75             sub clone {
76 7     7 1 5 my $self = shift;
77 7         577 my $clone = dclone( $self );
78 7         16 $clone->set_id( $self->_get_new_id );
79 7         11 return $clone;
80             }
81              
82              
83             # -- ACCESSORS
84              
85              
86             sub soss {
87 87     87 1 59 my $self = shift;
88             # Remember, the Stack Stack is up->bottom.
89 87 100       152 @_ and $self->get_ss->[0] = shift;
90 87         226 return $self->get_ss->[0];
91             }
92              
93              
94             sub scount {
95 73     73 1 68 my $self = shift;
96 73         56 return scalar @{ $self->get_toss };
  73         149  
97             }
98              
99             sub spush {
100 1556     1556 1 1394 my $self = shift;
101 1556         1019 push @{ $self->get_toss }, @_;
  1556         3801  
102             }
103              
104             sub spush_vec {
105 3     3 1 5 my ($self) = shift;
106 3         7 foreach my $v (@_) {
107 5         10 $self->spush($v->get_all_components);
108             }
109             }
110              
111             sub spush_args {
112 1     1 1 458 my $self = shift;
113 1         3 foreach my $arg ( @_ ) {
114             $self->spush
115             ( ($arg =~ /^-?\d+$/) ?
116             $arg # A number.
117 3 100       14 : reverse map {ord} split //, $arg.chr(0) # A string.
  8         8  
118             );
119             }
120             }
121              
122             sub spop {
123 1630     1630 1 2250886 my $self = shift;
124 1630         1094 my $val = pop @{ $self->get_toss };
  1630         1891  
125 1630 100       2425 defined $val or $val = 0;
126 1630         2930 return $val;
127             }
128              
129             sub spop_mult {
130 265     265 1 682 my ($self, $count) = @_;
131 265         371 my @rv = reverse map { $self->spop() } (1..$count);
  533         575  
132 265         557 return @rv;
133             }
134              
135             sub spop_vec {
136 35     35 1 30 my $self = shift;
137 35         96 return Language::Befunge::Vector->new($self->spop_mult($self->get_dims));
138             }
139              
140             sub spop_gnirts {
141 15     15 1 20 my $self = shift;
142 15         16 my ($val, $str);
143 15         17 do {
144 265         137 $val = pop @{ $self->get_toss };
  265         235  
145 265 100       304 defined $val or $val = 0;
146 265         360 $str .= chr($val);
147             } while( $val != 0 );
148 15         28 chop $str; # Remove trailing \0.
149 15         36 return $str;
150             }
151              
152             sub sclear {
153 8     8 1 11 my $self = shift;
154 8         23 $self->set_toss( [] );
155             }
156              
157             sub svalue {
158 6     6 1 9 my ($self, $idx) = @_;
159              
160 6         8 $idx = - abs( $idx );
161 6 100       20 return 0 unless exists $self->get_toss->[$idx];
162 5         16 return $self->get_toss->[$idx];
163             }
164              
165             sub ss_count {
166 97     97 1 627 my $self = shift;
167 97         62 return scalar( @{ $self->get_ss } );
  97         285  
168             }
169              
170             sub ss_create {
171 24     24 1 26 my ( $self, $n ) = @_;
172              
173 24         20 my @new_toss;
174              
175 24 100       65 if ( $n < 0 ) {
    100          
176             # Push zeroes on *current* toss (to-be soss).
177 2         6 $self->spush( (0) x abs($n) );
178             } elsif ( $n > 0 ) {
179 4         7 my $c = $n - $self->scount;
180 4 100       9 if ( $c <= 0 ) {
181             # Transfer elements.
182 2         2 @new_toss = splice @{ $self->get_toss }, -$n;
  2         8  
183             } else {
184             # Transfer elems and fill with zeroes.
185 2         4 @new_toss = ( (0) x $c, @{ $self->get_toss } );
  2         7  
186 2         8 $self->sclear;
187             }
188             }
189             # $n == 0: do nothing
190              
191              
192             # Push the former TOSS on the stack stack and copy reference to
193             # the new TOSS.
194             # For commodity reasons, the Stack Stack is oriented up->bottom
195             # (that is, a push is an unshift, and a pop is a shift).
196 24         20 unshift @{ $self->get_ss }, $self->get_toss;
  24         66  
197 24         56 $self->set_toss( \@new_toss );
198             }
199              
200             sub ss_remove {
201 15     15 1 184 my ( $self, $n ) = @_;
202              
203             # Fetch the TOSS.
204             # Remember, the Stack Stack is up->bottom.
205 15         16 my $new_toss = shift @{ $self->get_ss };
  15         27  
206              
207 15 100       47 if ( $n < 0 ) {
    100          
208             # Remove values.
209 3 100       9 if ( scalar(@$new_toss) >= abs($n) ) {
210 2         3 splice @$new_toss, $n;
211             } else {
212 1         2 $new_toss = [];
213             }
214             } elsif ( $n > 0 ) {
215 4         7 my $c = $n - $self->scount;
216 4 100       12 if ( $c <= 0 ) {
217             # Transfer elements.
218 2         3 push @$new_toss, splice( @{ $self->get_toss }, -$n );
  2         7  
219             } else {
220             # Transfer elems and fill with zeroes.
221 2         6 push @$new_toss, ( (0) x $c, @{ $self->get_toss } );
  2         6  
222             }
223             }
224             # $n == 0: do nothing
225              
226              
227             # Store the new TOSS.
228 15         42 $self->set_toss( $new_toss );
229             }
230              
231             sub ss_transfer {
232 15     15 1 15 my ($self, $n) = @_;
233 15 100       30 $n == 0 and return;
234              
235 13 100       22 if ( $n > 0 ) {
236             # Transfer from SOSS to TOSS.
237 5         10 my $c = $n - $self->soss_count;
238 5         6 my @elems;
239 5 100       11 if ( $c <= 0 ) {
240 3         5 @elems = splice @{ $self->soss }, -$n;
  3         6  
241             } else {
242 2         4 @elems = ( (0) x $c, @{ $self->soss } );
  2         4  
243 2         6 $self->soss_clear;
244             }
245 5         12 $self->spush( reverse @elems );
246              
247             } else {
248 8         11 $n = -$n;
249             # Transfer from TOSS to SOSS.
250 8         13 my $c = $n - $self->scount;
251 8         10 my @elems;
252 8 100       14 if ( $c <= 0 ) {
253 6         6 @elems = splice @{ $self->get_toss }, -$n;
  6         19  
254             } else {
255 2         24 @elems = ( (0) x $c, @{ $self->get_toss } );
  2         9  
256 2         4 $self->sclear;
257             }
258 8         16 $self->soss_push( reverse @elems );
259              
260             }
261             }
262              
263             sub ss_sizes {
264 31     31 1 21 my $self = shift;
265              
266 31         50 my @sizes = ( $self->scount );
267              
268             # Store the size of each stack.
269 31         39 foreach my $i ( 1..$self->ss_count ) {
270 16         9 push @sizes, scalar @{ $self->get_ss->[$i-1] };
  16         29  
271             }
272              
273 31         65 return @sizes;
274             }
275              
276              
277             sub soss_count {
278 19     19 1 21 my $self = shift;
279 19         14 return scalar( @{ $self->soss } );
  19         29  
280             }
281              
282             sub soss_push {
283 33     33 1 30 my $self = shift;
284 33         31 push @{ $self->soss }, @_;
  33         56  
285             }
286              
287              
288             sub soss_pop_mult {
289 11     11 1 15 my ($self, $count) = @_;
290 11         20 my @rv = reverse map { $self->soss_pop() } (1..$count);
  22         30  
291 11         29 return @rv;
292             }
293              
294             sub soss_push_vec {
295 2     2 1 2 my $self = shift;
296 2         4 foreach my $v (@_) {
297 2         4 $self->soss_push($v->get_all_components);
298             }
299             }
300              
301             sub soss_pop {
302 27     27 1 24 my $self = shift;
303 27         17 my $val = pop @{ $self->soss };
  27         33  
304 27 100       55 defined $val or $val = 0;
305 27         50 return $val;
306             }
307              
308             sub soss_pop_vec {
309 11     11 1 12 my $self = shift;
310 11         35 return Language::Befunge::Vector->new($self->soss_pop_mult($self->get_dims));
311             }
312              
313             sub soss_clear {
314 3     3 1 3 my $self = shift;
315 3         6 $self->soss( [] );
316             }
317              
318              
319              
320             sub dir_go_east {
321 134     134 1 379 my $self = shift;
322 134         277 $self->get_delta->clear;
323 134         276 $self->get_delta->set_component(0, 1);
324             }
325              
326             sub dir_go_west {
327 97     97 1 82 my $self = shift;
328 97         233 $self->get_delta->clear;
329 97         211 $self->get_delta->set_component(0, -1);
330             }
331              
332             sub dir_go_north {
333 29     29 1 34 my $self = shift;
334 29         84 $self->get_delta->clear;
335 29         90 $self->get_delta->set_component(1, -1);
336             }
337              
338             sub dir_go_south {
339 73     73 1 67 my $self = shift;
340 73         204 $self->get_delta->clear;
341 73         175 $self->get_delta->set_component(1, 1);
342             }
343              
344             sub dir_go_high {
345 3     3 1 4 my $self = shift;
346 3         12 $self->get_delta->clear;
347 3         12 $self->get_delta->set_component(2, 1);
348             }
349              
350             sub dir_go_low {
351 2     2 1 4 my $self = shift;
352 2         8 $self->get_delta->clear;
353 2         8 $self->get_delta->set_component(2, -1);
354             }
355              
356             sub dir_go_away {
357 9     9 1 17 my $self = shift;
358 9         13 my $nd = $self->get_dims;
359 9         115 my $dim = (0..$nd-1)[int(rand $nd)];
360 9         35 $self->get_delta->clear;
361 9         12 my $value = (-1, 1)[int(rand 2)];
362 9         21 $self->get_delta->set_component($dim, $value);
363             }
364              
365             sub dir_turn_left {
366 15     15 1 17 my $self = shift;
367 15         45 my $old_dx = $self->get_delta->get_component(0);
368 15         35 my $old_dy = $self->get_delta->get_component(1);
369 15         45 $self->get_delta->set_component(0, 0 + $old_dy);
370 15         39 $self->get_delta->set_component(1, 0 + $old_dx * -1);
371             }
372              
373             sub dir_turn_right {
374 15     15 1 17 my $self = shift;
375 15         41 my $old_dx = $self->get_delta->get_component(0);
376 15         34 my $old_dy = $self->get_delta->get_component(1);
377 15         46 $self->get_delta->set_component(0, 0 + $old_dy * -1);
378 15         32 $self->get_delta->set_component(1, 0 + $old_dx);
379             }
380              
381             sub dir_reverse {
382 1601     1601 1 1205 my $self = shift;
383 1601         2900 $self->set_delta(-$self->get_delta);
384             }
385              
386             sub load {
387 17     17 1 19 my ($self, $lib) = @_;
388              
389 17         33 my $libs = $self->get_libs;
390 17         55 foreach my $letter ( 'A' .. 'Z' ) {
391 442 100       946 next unless $lib->can($letter);
392 43         37 push @{ $libs->{$letter} }, $lib;
  43         65  
393             }
394             }
395              
396             sub unload {
397 8     8 1 10 my ($self, $lib) = @_;
398              
399 8         12 my $libs = $self->get_libs;
400 8         23 foreach my $letter ( 'A' .. 'Z' ) {
401 208 100       494 next unless $lib->can($letter);
402 26         18 pop @{ $libs->{$letter} };
  26         46  
403             }
404             }
405              
406             sub extdata {
407 2     2 1 3 my $self = shift;
408 2         2 my $lib = shift;
409 2 100       9 @_ ? $self->get_data->{$lib} = shift : $self->get_data->{$lib};
410             }
411              
412              
413             # -- PRIVATE METHODS
414              
415             #
416             # my $id = _get_new_id;
417             #
418             # Forge a new IP id, that will distinct it from the other IPs of the program.
419             #
420             my $id = 0;
421             sub _get_new_id {
422 506     506   1030 return $id++;
423             }
424              
425             1;
426              
427             __END__