Bring Ops.pm coverage up to 100%.
[language-befunge.git] / lib / Language / Befunge / Ops.pm
blobbd25b0f6123d4a47b79bc9355a86ebe89d955181
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::Ops;
11 require 5.010;
13 use strict;
14 use warnings;
16 use Config; # For the 'y' instruction.
19 =head1 NAME
21 Language::Befunge::Ops - definition of the various operations
24 =head1 DESCRIPTION
26 This module implements the various befunge operations. Not all those
27 operations will be supported by the interpreter though, it will depend
28 on the type of befunge chosen.
31 =head1 SUBROUTINES
33 =head2 Numbers
35 =over 4
37 =item num_push_number( )
39 Push the current number onto the TOSS.
41 =cut
42 sub num_push_number {
43 my ($lbi) = @_;
45 # Fetching char.
46 my $ip = $lbi->get_curip;
47 my $num = hex( chr( $lbi->storage->get_value( $ip->get_position ) ) );
49 # Pushing value.
50 $ip->spush( $num );
52 # Cosmetics.
53 $lbi->debug( "pushing number '$num'\n" );
56 =back
60 =head2 Strings
62 =over 4
64 =item str_enter_string_mode( )
66 =cut
67 sub str_enter_string_mode {
68 my ($lbi) = @_;
70 # Cosmetics.
71 $lbi->debug( "entering string mode\n" );
73 # Entering string-mode.
74 $lbi->get_curip->set_string_mode(1);
78 =item str_fetch_char( )
80 =cut
81 sub str_fetch_char {
82 my ($lbi) = @_;
83 my $ip = $lbi->get_curip;
85 # Moving pointer...
86 $lbi->move_ip($lbi->get_curip);
88 # .. then fetch value and push it.
89 my $ord = $lbi->storage->get_value( $ip->get_position );
90 my $chr = $lbi->storage->get_char( $ip->get_position );
91 $ip->spush( $ord );
93 # Cosmetics.
94 $lbi->debug( "pushing value $ord (char='$chr')\n" );
98 =item str_store_char( )
100 =cut
101 sub str_store_char {
102 my ($lbi) = @_;
103 my $ip = $lbi->get_curip;
105 # Moving pointer.
106 $lbi->move_ip($lbi->get_curip);
108 # Fetching value.
109 my $val = $ip->spop;
111 # Storing value.
112 $lbi->storage->set_value( $ip->get_position, $val );
113 my $chr = $lbi->storage->get_char( $ip->get_position );
115 # Cosmetics.
116 $lbi->debug( "storing value $val (char='$chr')\n" );
119 =back
123 =head2 Mathematical operations
125 =over 4
127 =item math_addition( )
129 =cut
130 sub math_addition {
131 my ($lbi) = @_;
132 my $ip = $lbi->get_curip;
134 # Fetching values.
135 my ($v1, $v2) = $ip->spop_mult(2);
136 $lbi->debug( "adding: $v1+$v2\n" );
137 my $res = $v1 + $v2;
139 # Checking over/underflow.
140 $res > 2**31-1 and $lbi->abort( "program overflow while performing addition" );
141 $res < -2**31 and $lbi->abort( "program underflow while performing addition" );
143 # Pushing value.
144 $ip->spush( $res );
148 =item math_substraction( )
150 =cut
151 sub math_substraction {
152 my ($lbi) = @_;
153 my $ip = $lbi->get_curip;
155 # Fetching values.
156 my ($v1, $v2) = $ip->spop_mult(2);
157 $lbi->debug( "substracting: $v1-$v2\n" );
158 my $res = $v1 - $v2;
160 # checking over/underflow.
161 $res > 2**31-1 and $lbi->abort( "program overflow while performing substraction" );
162 $res < -2**31 and $lbi->abort( "program underflow while performing substraction" );
164 # Pushing value.
165 $ip->spush( $res );
169 =item math_multiplication( )
171 =cut
172 sub math_multiplication {
173 my ($lbi) = @_;
174 my $ip = $lbi->get_curip;
176 # Fetching values.
177 my ($v1, $v2) = $ip->spop_mult(2);
178 $lbi->debug( "multiplicating: $v1*$v2\n" );
179 my $res = $v1 * $v2;
181 # checking over/underflow.
182 $res > 2**31-1 and $lbi->abort( "program overflow while performing multiplication" );
183 $res < -2**31 and $lbi->abort( "program underflow while performing multiplication" );
185 # Pushing value.
186 $ip->spush( $res );
190 =item math_division( )
192 =cut
193 sub math_division {
194 my ($lbi) = @_;
195 my $ip = $lbi->get_curip;
197 # Fetching values.
198 my ($v1, $v2) = $ip->spop_mult(2);
199 $lbi->debug( "dividing: $v1/$v2\n" );
200 my $res = $v2 == 0 ? 0 : int($v1 / $v2);
202 # Can't do over/underflow with integer division.
204 # Pushing value.
205 $ip->spush( $res );
209 =item math_remainder( )
211 =cut
212 sub math_remainder {
213 my ($lbi) = @_;
214 my $ip = $lbi->get_curip;
216 # Fetching values.
217 my ($v1, $v2) = $ip->spop_mult(2);
218 $lbi->debug( "remainder: $v1%$v2\n" );
219 my $res = $v2 == 0 ? 0 : int($v1 % $v2);
221 # Can't do over/underflow with integer remainder.
223 # Pushing value.
224 $ip->spush( $res );
227 =back
231 =head2 Direction changing
233 =over 4
235 =item dir_go_east( )
237 =cut
238 sub dir_go_east {
239 my ($lbi) = @_;
240 $lbi->debug( "going east\n" );
241 $lbi->get_curip->dir_go_east;
245 =item dir_go_west( )
247 =cut
248 sub dir_go_west {
249 my ($lbi) = @_;
250 $lbi->debug( "going west\n" );
251 $lbi->get_curip->dir_go_west;
255 =item dir_go_north( )
257 =cut
258 sub dir_go_north {
259 my ($lbi) = @_;
260 $lbi->debug( "going north\n" );
261 $lbi->get_curip->dir_go_north;
265 =item dir_go_south( )
267 =cut
268 sub dir_go_south {
269 my ($lbi) = @_;
270 $lbi->debug( "going south\n" );
271 $lbi->get_curip->dir_go_south;
275 =item dir_go_high( )
277 =cut
278 sub dir_go_high {
279 my ($lbi) = @_;
280 $lbi->debug( "going high\n" );
281 $lbi->get_curip->dir_go_high;
285 =item dir_go_low( )
287 =cut
288 sub dir_go_low {
289 my ($lbi) = @_;
290 $lbi->debug( "going low\n" );
291 $lbi->get_curip->dir_go_low;
295 =item dir_go_away( )
297 =cut
298 sub dir_go_away {
299 my ($lbi) = @_;
300 $lbi->debug( "going away!\n" );
301 $lbi->get_curip->dir_go_away;
305 =item dir_turn_left( )
307 Turning left, like a car (the specs speak about a bicycle, but perl
308 is _so_ fast that we can speak about cars ;) ).
310 =cut
311 sub dir_turn_left {
312 my ($lbi) = @_;
313 $lbi->debug( "turning on the left\n" );
314 $lbi->get_curip->dir_turn_left;
318 =item dir_turn_right( )
320 Turning right, like a car (the specs speak about a bicycle, but perl
321 is _so_ fast that we can speak about cars ;) ).
323 =cut
324 sub dir_turn_right {
325 my ($lbi) = @_;
326 $lbi->debug( "turning on the right\n" );
327 $lbi->get_curip->dir_turn_right;
331 =item dir_reverse( )
333 =cut
334 sub dir_reverse {
335 my ($lbi) = @_;
336 $lbi->debug( "180 deg!\n" );
337 $lbi->get_curip->dir_reverse;
341 =item dir_set_delta( )
343 Hmm, the user seems to know where he wants to go. Let's trust him/her.
345 =cut
346 sub dir_set_delta {
347 my ($lbi) = @_;
348 my $ip = $lbi->get_curip;
349 my ($new_d) = $ip->spop_vec;
350 $lbi->debug( "setting delta to $new_d\n" );
351 $ip->set_delta( $new_d );
354 =back
358 =head2 Decision making
360 =over 4
362 =item decis_neg( )
364 =cut
365 sub decis_neg {
366 my ($lbi) = @_;
367 my $ip = $lbi->get_curip;
369 # Fetching value.
370 my $val = $ip->spop ? 0 : 1;
371 $ip->spush( $val );
373 $lbi->debug( "logical not: pushing $val\n" );
377 =item decis_gt( )
379 =cut
380 sub decis_gt {
381 my ($lbi) = @_;
382 my $ip = $lbi->get_curip;
384 # Fetching values.
385 my ($v1, $v2) = $ip->spop_mult(2);
386 $lbi->debug( "comparing $v1 vs $v2\n" );
387 $ip->spush( ($v1 > $v2) ? 1 : 0 );
391 =item decis_horiz_if( )
393 =cut
394 sub decis_horiz_if {
395 my ($lbi) = @_;
396 my $ip = $lbi->get_curip;
398 # Fetching value.
399 my $val = $ip->spop;
400 $val ? $ip->dir_go_west : $ip->dir_go_east;
401 $lbi->debug( "horizontal if: going " . ( $val ? "west\n" : "east\n" ) );
405 =item decis_vert_if( )
407 =cut
408 sub decis_vert_if {
409 my ($lbi) = @_;
410 my $ip = $lbi->get_curip;
412 # Fetching value.
413 my $val = $ip->spop;
414 $val ? $ip->dir_go_north : $ip->dir_go_south;
415 $lbi->debug( "vertical if: going " . ( $val ? "north\n" : "south\n" ) );
419 =item decis_z_if( )
421 =cut
422 sub decis_z_if {
423 my ($lbi) = @_;
424 my $ip = $lbi->get_curip;
426 # Fetching value.
427 my $val = $ip->spop;
428 $val ? $ip->dir_go_low : $ip->dir_go_high;
429 $lbi->debug( "z if: going " . ( $val ? "low\n" : "high\n" ) );
433 =item decis_cmp( )
435 =cut
436 sub decis_cmp {
437 my ($lbi) = @_;
438 my $ip = $lbi->get_curip;
440 # Fetching value.
441 my ($v1, $v2) = $ip->spop_mult(2);
442 $lbi->debug( "comparing $v1 with $v2: straight forward!\n"), return if $v1 == $v2;
444 my $dir;
445 if ( $v1 < $v2 ) {
446 $ip->dir_turn_left;
447 $dir = "left";
448 } else {
449 $ip->dir_turn_right;
450 $dir = "right";
452 $lbi->debug( "comparing $v1 with $v2: turning: $dir\n" );
455 =back
459 =head2 Flow control
461 =over 4
463 =item flow_space( )
465 A serie of spaces is to be treated as B<one> NO-OP.
467 =cut
468 sub flow_space {
469 my ($lbi) = @_;
470 $lbi->move_ip( $lbi->get_curip, qr/ / );
471 $lbi->debug( "slurping serie of spaces\n" );
475 =item flow_no_op( )
477 =cut
478 sub flow_no_op {
479 my ($lbi) = @_;
480 $lbi->debug( "no-op\n" );
484 =item flow_comments( )
486 Bypass comments in one tick.
488 =cut
489 sub flow_comments {
490 my ($lbi) = @_;
491 $lbi->move_ip($lbi->get_curip);
492 $lbi->move_ip($lbi->get_curip, qr/[^;]/);
493 $lbi->move_ip($lbi->get_curip);
494 $lbi->debug( "skipping comments\n" );
498 =item flow_trampoline( )
500 =cut
501 sub flow_trampoline {
502 my ($lbi) = @_;
503 $lbi->move_ip($lbi->get_curip);
504 $lbi->debug( "trampoline! (skipping next instruction)\n" );
508 =item flow_jump_to( )
510 =cut
511 sub flow_jump_to {
512 my ($lbi) = @_;
513 my $ip = $lbi->get_curip;
514 my $count = $ip->spop;
515 $lbi->debug( "skipping $count instructions\n" );
516 $count == 0 and return;
517 $count < 0 and $ip->dir_reverse; # We can move backward.
518 $lbi->move_ip($lbi->get_curip) for (1..abs($count));
519 # don't forget that runloop will advance the ip next time.
520 $count < 0 and $lbi->move_ip($lbi->get_curip), $ip->dir_reverse;
524 =item flow_repeat( )
526 =cut
527 sub flow_repeat {
528 my ($lbi) = @_;
529 my $ip = $lbi->get_curip;
531 my $kcounter = $ip->spop;
532 $lbi->debug( "repeating next instruction $kcounter times.\n" );
533 $lbi->move_ip($lbi->get_curip);
535 # Nothing to repeat.
536 $kcounter == 0 and return;
538 # Ooops, error.
539 $kcounter < 0 and $lbi->abort( "Attempt to repeat ('k') a negative number of times ($kcounter)" );
541 # Fetch instruction to repeat.
542 my $val = $lbi->storage->get_value( $ip->get_position );
544 # Check if we can repeat the instruction.
545 $val > 0 and $val < 256 and chr($val) =~ /([ ;])/ and
546 $lbi->abort( "Attempt to repeat ('k') a forbidden instruction ('$1')" );
547 $val > 0 and $val < 256 and chr($val) eq "k" and
548 $lbi->abort( "Attempt to repeat ('k') a repeat instruction ('k')" );
550 $lbi->process_ip(0) for (1..$kcounter);
554 =item flow_kill_thread( )
556 =cut
557 sub flow_kill_thread {
558 my ($lbi) = @_;
559 $lbi->debug( "end of Instruction Pointer\n" );
560 $lbi->get_curip->set_end('@');
564 =item flow_quit( )
566 =cut
567 sub flow_quit {
568 my ($lbi) = @_;
569 $lbi->debug( "end program\n" );
570 $lbi->set_newips( [] );
571 $lbi->set_ips( [] );
572 $lbi->get_curip->set_end('q');
573 $lbi->set_retval( $lbi->get_curip->spop );
576 =back
580 =head2 Stack manipulation
582 =over 4
584 =item stack_pop( )
586 =cut
587 sub stack_pop {
588 my ($lbi) = @_;
589 $lbi->debug( "popping a value\n" );
590 $lbi->get_curip->spop;
594 =item stack_duplicate( )
596 =cut
597 sub stack_duplicate {
598 my ($lbi) = @_;
599 my $ip = $lbi->get_curip;
600 my $value = $ip->spop;
601 $lbi->debug( "duplicating value '$value'\n" );
602 $ip->spush( $value );
603 $ip->spush( $value );
607 =item stack_swap( )
609 =cut
610 sub stack_swap {
611 my ($lbi) = @_;
612 my $ ip = $lbi->get_curip;
613 my ($v1, $v2) = $ip->spop_mult(2);
614 $lbi->debug( "swapping $v1 and $v2\n" );
615 $ip->spush( $v2 );
616 $ip->spush( $v1 );
620 =item stack_clear( )
622 =cut
623 sub stack_clear {
624 my ($lbi) = @_;
625 $lbi->debug( "clearing stack\n" );
626 $lbi->get_curip->sclear;
629 =back
633 =head2 Stack stack manipulation
635 =over 4
637 =item block_open( )
639 =cut
640 sub block_open {
641 my ($lbi) = @_;
642 my $ip = $lbi->get_curip;
643 $lbi->debug( "block opening\n" );
645 # Create new TOSS.
646 $ip->ss_create( $ip->spop );
648 # Store current storage offset on SOSS.
649 $ip->soss_push( $ip->get_storage->get_all_components );
651 # Set the new Storage Offset.
652 $lbi->move_ip($lbi->get_curip);
653 $ip->set_storage( $ip->get_position );
654 $ip->dir_reverse;
655 $lbi->move_ip($lbi->get_curip);
656 $ip->dir_reverse;
660 =item block_close( )
662 =cut
663 sub block_close {
664 my ($lbi) = @_;
665 my $ip = $lbi->get_curip;
667 # No opened block.
668 $ip->ss_count <= 0 and $ip->dir_reverse, $lbi->debug("no opened block\n"), return;
670 $lbi->debug( "block closing\n" );
672 # Restore Storage offset.
673 $ip->set_storage( $ip->soss_pop_vec );
675 # Remove the TOSS.
676 $ip->ss_remove( $ip->spop );
680 =item bloc_transfer( )
682 =cut
683 sub bloc_transfer {
684 my ($lbi) = @_;
685 my $ip = $lbi->get_curip;
687 $ip->ss_count <= 0 and $ip->dir_reverse, $lbi->debug("no SOSS available\n"), return;
689 # Transfering values.
690 $lbi->debug( "transfering values\n" );
691 $ip->ss_transfer( $ip->spop );
694 =back
698 =head2 Funge-space storage
700 =over 4
702 =item store_get( )
704 =cut
705 sub store_get {
706 my ($lbi) = @_;
707 my $ip = $lbi->get_curip;
709 # Fetching coordinates.
710 my ($v) = $ip->spop_vec;
711 $v += $ip->get_storage;
713 # Fetching char.
714 my $val = $lbi->storage->get_value( $v );
715 $ip->spush( $val );
717 $lbi->debug( "fetching value at $v: pushing $val\n" );
721 =item store_put( )
723 =cut
724 sub store_put {
725 my ($lbi) = @_;
726 my $ip = $lbi->get_curip;
728 # Fetching coordinates.
729 my ($v) = $ip->spop_vec;
730 $v += $ip->get_storage;
732 # Fetching char.
733 my $val = $ip->spop;
734 $lbi->storage->set_value( $v, $val );
736 $lbi->debug( "storing value $val at $v\n" );
739 =back
743 =head2 Standard Input/Output
745 =over 4
747 =item stdio_out_num( )
749 =cut
750 sub stdio_out_num {
751 my ($lbi) = @_;
752 my $ip = $lbi->get_curip;
754 # Fetch value and print it.
755 my $val = $ip->spop;
756 $lbi->debug( "numeric output: $val\n");
757 print( "$val " ) or $ip->dir_reverse;
761 =item stdio_out_ascii( )
763 =cut
764 sub stdio_out_ascii {
765 my ($lbi) = @_;
766 my $ip = $lbi->get_curip;
768 # Fetch value and print it.
769 my $val = $ip->spop;
770 my $chr = chr $val;
771 $lbi->debug( "ascii output: '$chr' (ord=$val)\n");
772 print( $chr ) or $ip->dir_reverse;
776 =item stdio_in_num( )
778 =cut
779 sub stdio_in_num {
780 my ($lbi) = @_;
781 my $ip = $lbi->get_curip;
782 my ($in, $nb) = ('', 0);
783 my $last = 0;
784 while(!$last) {
785 my $char = $lbi->get_input();
786 $in .= $char if defined $char;
787 my $overflow;
788 ($nb, $overflow) = $in =~ /(-?\d+)(\D*)$/;
789 if((defined($overflow) && length($overflow)) || !defined($char)) {
790 # either we found a non-digit character: $overflow
791 # or else we reached EOF: !$char
792 return $ip->dir_reverse() unless defined $nb;
793 $nb < -2**31 and $nb = -2**31;
794 $nb > 2**31-1 and $nb = 2**31-1;
795 $in = $overflow;
796 $last++;
799 $lbi->set_input( $in );
800 $ip->spush( $nb );
801 $lbi->debug( "numeric input: pushing $nb\n" );
805 =item stdio_in_ascii( )
807 =cut
808 sub stdio_in_ascii {
809 my ($lbi) = @_;
810 my $ip = $lbi->get_curip;
811 my $in = $lbi->get_input();
812 return $ip->dir_reverse unless defined $in;
813 my $ord = ord $in;
814 $ip->spush( $ord );
815 $lbi->debug( "ascii input: pushing $ord\n" );
819 =item stdio_in_file( )
821 =cut
822 sub stdio_in_file {
823 my ($lbi) = @_;
824 my $ip = $lbi->get_curip;
826 # Fetch arguments.
827 my $path = $ip->spop_gnirts;
828 my $flag = $ip->spop;
829 my ($vin) = $ip->spop_vec;
830 $vin += $ip->get_storage;
832 # Read file.
833 $lbi->debug( "input file '$path' at $vin\n" );
834 open F, "<", $path or $ip->dir_reverse, return;
835 my $lines;
837 local $/; # slurp mode.
838 $lines = <F>;
840 close F;
842 # Store the code and the result vector.
843 my ($size) = $flag % 2
844 ? ( $lbi->storage->store_binary( $lines, $vin ) )
845 : ( $lbi->storage->store( $lines, $vin ) );
846 $ip->spush_vec( $size, $vin );
850 =item stdio_out_file( )
852 =cut
853 sub stdio_out_file {
854 my ($lbi) = @_;
855 my $ip = $lbi->get_curip;
857 # Fetch arguments.
858 my $path = $ip->spop_gnirts;
859 my $flag = $ip->spop;
860 my ($vin) = $ip->spop_vec;
861 $vin += $ip->get_storage;
862 my ($size) = $ip->spop_vec;
863 my $data = $lbi->storage->rectangle( $vin, $size );
865 # Cosmetics.
866 my $vend = $vin + $size;
867 $lbi->debug( "output $vin-$vend to '$path'\n" );
869 # Treat the data chunk as text file?
870 if ( $flag & 0x1 ) {
871 $data =~ s/ +$//mg; # blank lines are now void.
872 $data =~ s/\n+\z/\n/; # final blank lines are stripped.
875 # Write file.
876 open F, ">", $path or $ip->dir_reverse, return;
877 print F $data;
878 close F;
882 =item stdio_sys_exec( )
884 =cut
885 sub stdio_sys_exec {
886 my ($lbi) = @_;
887 my $ip = $lbi->get_curip;
889 # Fetching command.
890 my $path = $ip->spop_gnirts;
891 $lbi->debug( "spawning external command: $path\n" );
892 system( $path );
893 $ip->spush( $? == -1 ? -1 : $? >> 8 );
896 =back
900 =head2 System info retrieval
902 =over 4
904 =item sys_info( )
906 =cut
907 sub sys_info {
908 my ($lbi) = @_;
909 my $ip = $lbi->get_curip;
910 my $storage = $lbi->storage;
912 my $val = $ip->spop;
913 my @cells = ();
915 # 1. flags
916 push @cells, 0x01 # 't' is implemented.
917 | 0x02 # 'i' is implemented.
918 | 0x04 # 'o' is implemented.
919 | 0x08 # '=' is implemented.
920 | !0x10; # buffered IO (non getch).
922 # 2. number of bytes per cell.
923 # 32 bytes Funge: 4 bytes.
924 push @cells, 4;
926 # 3. implementation handprint.
927 my @hand = reverse map { ord } split //, $lbi->get_handprint . chr(0);
928 push @cells, \@hand;
930 # 4. version number.
931 my $ver = $Language::Befunge::VERSION;
932 $ver =~ s/\D//g;
933 push @cells, $ver;
935 # 5. ID code for Operating Paradigm.
936 push @cells, 1; # C-language system() call behaviour.
938 # 6. Path separator character.
939 push @cells, ord( $Config{path_sep} );
941 # 7. Number of dimensions.
942 push @cells, $ip->get_dims;
944 # 8. Unique IP number.
945 push @cells, $ip->get_id;
947 # 9. Concurrent Funge (not implemented).
948 push @cells, 0;
950 # 10. Position of the curent IP.
951 my @pos = ( $ip->get_position->get_all_components );
952 push @cells, \@pos;
954 # 11. Delta of the curent IP.
955 my @delta = ( $ip->get_delta->get_all_components );
956 push @cells, \@delta;
958 # 12. Storage offset of the curent IP.
959 my @stor = ( $ip->get_storage->get_all_components );
960 push @cells, \@stor;
962 # 13. Top-left point.
963 my $min = $storage->min;
964 # FIXME: multiple dims?
965 my @topleft = ( $min->get_component(0), $min->get_component(1) );
966 push @cells, \@topleft;
968 # 14. Dims of the storage.
969 my $max = $storage->max;
970 # FIXME: multiple dims?
971 my @dims = ( $max->get_component(0) - $min->get_component(0) + 1,
972 $max->get_component(1) - $min->get_component(1) + 1 );
973 push @cells, \@dims;
975 # 15/16. Current date/time.
976 my ($s,$m,$h,$dd,$mm,$yy)=localtime;
977 push @cells, $yy*256*256 + $mm*256 + $dd;
978 push @cells, $h*256*256 + $m*256 + $s;
980 # 17. Size of stack stack.
981 push @cells, $ip->ss_count + 1;
983 # 18. Size of each stack in the stack stack.
984 # !!FIXME!! Funge specs just tell to push onto the
985 # stack the size of the stacks, but nothing is
986 # said about how user will retrieve the number of
987 # stacks.
988 my @sizes = reverse $ip->ss_sizes;
989 push @cells, \@sizes;
991 # 19. $file + params.
992 my $str = join chr(0), $lbi->get_file, @{$lbi->get_params}, chr(0);
993 my @cmdline = reverse map { ord } split //, $str;
994 push @cells, \@cmdline;
996 # 20. %ENV
997 # 00EULAV=EMAN0EULAV=EMAN
998 $str = "";
999 $str .= "$_=$ENV{$_}".chr(0) foreach sort keys %ENV;
1000 $str .= chr(0);
1001 my @env = reverse map { ord } split //, $str;
1002 push @cells, \@env;
1004 # Okay, what to do with those cells.
1005 if ( $val <= 0 ) {
1006 # Blindly push them onto the stack.
1007 $lbi->debug( "system info: pushing the whole stuff\n" );
1008 foreach my $cell ( reverse @cells ) {
1009 $ip->spush( ref( $cell ) eq "ARRAY" ?
1010 @$cell : $cell );
1013 } elsif ( $val <= 20 ) {
1014 # Only push the wanted value.
1015 $lbi->debug( "system info: pushing the ${val}th value\n" );
1016 $ip->spush( ref( $cells[$val-1] ) eq "ARRAY" ?
1017 @{ $cells[$val-1] } : $cells[$val-1] );
1019 } else {
1020 # Pick a given value in the stack and push it.
1021 my $offset = $val - 20;
1022 my $value = $ip->svalue($offset);
1023 $lbi->debug( "system info: picking the ${offset}th value from the stack = $value\n" );
1024 $ip->spush( $value );
1028 =back
1032 =head2 Concurrent Funge
1034 =over 4
1036 =item spawn_ip( )
1038 =cut
1039 sub spawn_ip {
1040 my ($lbi) = @_;
1042 # Cosmetics.
1043 $lbi->debug( "spawning new IP\n" );
1045 # Cloning and storing new IP.
1046 my $newip = $lbi->get_curip->clone;
1047 $newip->dir_reverse;
1048 $lbi->move_ip($newip);
1049 push @{ $lbi->get_newips }, $newip;
1052 =back
1056 =head2 Library semantics
1058 =over 4
1060 =item lib_load( )
1062 =cut
1063 sub lib_load {
1064 my ($lbi) = @_;
1065 my $ip = $lbi->get_curip;
1067 # Fetching fingerprint.
1068 my $count = $ip->spop;
1069 my $fgrprt = 0;
1070 while ( $count-- > 0 ) {
1071 my $val = $ip->spop;
1072 $lbi->abort( "Attempt to build a fingerprint with a negative number" )
1073 if $val < 0;
1074 $fgrprt = $fgrprt * 256 + $val;
1077 # Transform the fingerprint into a library name.
1078 my $lib = "";
1079 my $finger = $fgrprt;
1080 while ( $finger > 0 ) {
1081 my $c = $finger % 0x100;
1082 $lib .= chr($c);
1083 $finger = int ( $finger / 0x100 );
1085 $lib = "Language::Befunge::lib::" . reverse $lib;
1087 # Checking if library exists.
1088 eval "require $lib";
1089 if ( $@ ) {
1090 $lbi->debug( sprintf("unknown extension $lib (0x%x): reversing\n", $fgrprt) );
1091 $ip->dir_reverse;
1092 } else {
1093 $lbi->debug( sprintf("extension $lib (0x%x) loaded\n", $fgrprt) );
1094 my $obj = new $lib;
1095 $ip->load( $obj );
1096 $ip->spush( $fgrprt, 1 );
1101 =item lib_unload( )
1103 =cut
1104 sub lib_unload {
1105 my ($lbi) = @_;
1106 my $ip = $lbi->get_curip;
1108 # Fetching fingerprint.
1109 my $count = $ip->spop;
1110 my $fgrprt = 0;
1111 while ( $count-- > 0 ) {
1112 my $val = $ip->spop;
1113 $lbi->abort( "Attempt to build a fingerprint with a negative number" )
1114 if $val < 0;
1115 $fgrprt = $fgrprt * 256 + $val;
1118 # Transform the fingerprint into a library name.
1119 my $lib = "";
1120 my $finger = $fgrprt;
1121 while ( $finger > 0 ) {
1122 my $c = $finger % 0x100;
1123 $lib .= chr($c);
1124 $finger = int ( $finger / 0x100 );
1126 $lib = "Language::Befunge::lib::" . reverse $lib;
1128 # Unload the library.
1129 if ( defined( $ip->unload($lib) ) ) {
1130 $lbi->debug( sprintf("unloading library $lib (0x%x)\n", $fgrprt) );
1131 } else {
1132 # The library wasn't loaded.
1133 $lbi->debug( sprintf("library $lib (0x%x) wasn't loaded\n", $fgrprt) );
1134 $ip->dir_reverse;
1138 =item lib_run_instruction( )
1140 =cut
1142 sub lib_run_instruction {
1143 my ($lbi) = @_;
1144 my $ip = $lbi->get_curip;
1145 my $char = $lbi->storage->get_char( $ip->get_position );
1147 # Maybe a library semantics.
1148 $lbi->debug( "library semantics\n" );
1150 foreach my $obj ( @{ $ip->get_libs } ) {
1151 # Try the loaded libraries in order.
1152 eval "\$obj->$char(\$lbi)";
1153 if( $@ ) {
1154 $lbi->debug( ref($obj) . "->$char failed: $@" );
1155 next;
1158 # We manage to get a library.
1159 $lbi->debug( "library semantics processed by ".ref($obj)."\n" );
1160 return;
1163 # Non-overloaded capitals default to reverse.
1164 $lbi->debug("no library semantics found: reversing\n");
1165 $ip->dir_reverse;
1168 =back
1170 =cut
1174 __END__
1176 =head1 SEE ALSO
1178 L<Language::Befunge>
1181 =head1 AUTHOR
1183 Jerome Quelin, E<lt>jquelin@cpan.orgE<gt>
1185 Development is discussed on E<lt>language-befunge@mongueurs.netE<gt>
1188 =head1 COPYRIGHT & LICENSE
1190 Copyright (c) 2001-2008 Jerome Quelin, all rights reserved.
1192 This program is free software; you can redistribute it and/or modify
1193 it under the same terms as Perl itself.
1196 =cut