File Coverage

blib/lib/Language/Befunge/IP.pm
Criterion Covered Total %
statement 208 208 100.0
branch 42 42 100.0
condition n/a
subroutine 46 46 100.0
pod 38 38 100.0
total 334 334 100.0


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