v4.13
[language-befunge.git] / lib / Language / Befunge / Ops.pm
blob322d6144865d113b499cd3501a36cca242781be7
2 # This file is part of Language::Befunge.
3 # Copyright (c) 2001-2009 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 File::Spec::Functions qw{ catfile }; # For the 'y' instruction.
17 use Language::Befunge::Debug;
20 =head1 NAME
22 Language::Befunge::Ops - definition of the various operations
25 =head1 DESCRIPTION
27 This module implements the various befunge operations. Not all those
28 operations will be supported by the interpreter though, it will depend
29 on the type of befunge chosen.
32 =head1 SUBROUTINES
34 =head2 Numbers
36 =over 4
38 =item num_push_number( )
40 Push the current number onto the TOSS.
42 =cut
43 sub num_push_number {
44 my ($lbi, $char) = @_;
46 # Fetching char.
47 my $ip = $lbi->get_curip;
48 my $num = hex( $char );
50 # Pushing value.
51 $ip->spush( $num );
53 # Cosmetics.
54 debug( "pushing number '$num'\n" );
57 =back
61 =head2 Strings
63 =over 4
65 =item str_enter_string_mode( )
67 =cut
68 sub str_enter_string_mode {
69 my ($lbi) = @_;
71 # Cosmetics.
72 debug( "entering string mode\n" );
74 # Entering string-mode.
75 $lbi->get_curip->set_string_mode(1);
79 =item str_fetch_char( )
81 =cut
82 sub str_fetch_char {
83 my ($lbi) = @_;
84 my $ip = $lbi->get_curip;
86 # Moving pointer...
87 $lbi->_move_ip_once($lbi->get_curip);
89 # .. then fetch value and push it.
90 my $ord = $lbi->get_storage->get_value( $ip->get_position );
91 my $chr = $lbi->get_storage->get_char( $ip->get_position );
92 $ip->spush( $ord );
94 # Cosmetics.
95 debug( "pushing value $ord (char='$chr')\n" );
99 =item str_store_char( )
101 =cut
102 sub str_store_char {
103 my ($lbi) = @_;
104 my $ip = $lbi->get_curip;
106 # Moving pointer.
107 $lbi->_move_ip_once($lbi->get_curip);
109 # Fetching value.
110 my $val = $ip->spop;
112 # Storing value.
113 $lbi->get_storage->set_value( $ip->get_position, $val );
114 my $chr = $lbi->get_storage->get_char( $ip->get_position );
116 # Cosmetics.
117 debug( "storing value $val (char='$chr')\n" );
120 =back
124 =head2 Mathematical operations
126 =over 4
128 =item math_addition( )
130 =cut
131 sub math_addition {
132 my ($lbi) = @_;
133 my $ip = $lbi->get_curip;
135 # Fetching values.
136 my ($v1, $v2) = $ip->spop_mult(2);
137 debug( "adding: $v1+$v2\n" );
138 my $res = $v1 + $v2;
140 # Checking over/underflow.
141 $res > 2**31-1 and $lbi->abort( "program overflow while performing addition" );
142 $res < -2**31 and $lbi->abort( "program underflow while performing addition" );
144 # Pushing value.
145 $ip->spush( $res );
149 =item math_substraction( )
151 =cut
152 sub math_substraction {
153 my ($lbi) = @_;
154 my $ip = $lbi->get_curip;
156 # Fetching values.
157 my ($v1, $v2) = $ip->spop_mult(2);
158 debug( "substracting: $v1-$v2\n" );
159 my $res = $v1 - $v2;
161 # checking over/underflow.
162 $res > 2**31-1 and $lbi->abort( "program overflow while performing substraction" );
163 $res < -2**31 and $lbi->abort( "program underflow while performing substraction" );
165 # Pushing value.
166 $ip->spush( $res );
170 =item math_multiplication( )
172 =cut
173 sub math_multiplication {
174 my ($lbi) = @_;
175 my $ip = $lbi->get_curip;
177 # Fetching values.
178 my ($v1, $v2) = $ip->spop_mult(2);
179 debug( "multiplicating: $v1*$v2\n" );
180 my $res = $v1 * $v2;
182 # checking over/underflow.
183 $res > 2**31-1 and $lbi->abort( "program overflow while performing multiplication" );
184 $res < -2**31 and $lbi->abort( "program underflow while performing multiplication" );
186 # Pushing value.
187 $ip->spush( $res );
191 =item math_division( )
193 =cut
194 sub math_division {
195 my ($lbi) = @_;
196 my $ip = $lbi->get_curip;
198 # Fetching values.
199 my ($v1, $v2) = $ip->spop_mult(2);
200 debug( "dividing: $v1/$v2\n" );
201 my $res = $v2 == 0 ? 0 : int($v1 / $v2);
203 # Can't do over/underflow with integer division.
205 # Pushing value.
206 $ip->spush( $res );
210 =item math_remainder( )
212 =cut
213 sub math_remainder {
214 my ($lbi) = @_;
215 my $ip = $lbi->get_curip;
217 # Fetching values.
218 my ($v1, $v2) = $ip->spop_mult(2);
219 debug( "remainder: $v1%$v2\n" );
220 my $res = $v2 == 0 ? 0 : int($v1 % $v2);
222 # Can't do over/underflow with integer remainder.
224 # Pushing value.
225 $ip->spush( $res );
228 =back
232 =head2 Direction changing
234 =over 4
236 =item dir_go_east( )
238 =cut
239 sub dir_go_east {
240 my ($lbi) = @_;
241 debug( "going east\n" );
242 $lbi->get_curip->dir_go_east;
246 =item dir_go_west( )
248 =cut
249 sub dir_go_west {
250 my ($lbi) = @_;
251 debug( "going west\n" );
252 $lbi->get_curip->dir_go_west;
256 =item dir_go_north( )
258 =cut
259 sub dir_go_north {
260 my ($lbi) = @_;
261 debug( "going north\n" );
262 $lbi->get_curip->dir_go_north;
266 =item dir_go_south( )
268 =cut
269 sub dir_go_south {
270 my ($lbi) = @_;
271 debug( "going south\n" );
272 $lbi->get_curip->dir_go_south;
276 =item dir_go_high( )
278 =cut
279 sub dir_go_high {
280 my ($lbi) = @_;
281 debug( "going high\n" );
282 $lbi->get_curip->dir_go_high;
286 =item dir_go_low( )
288 =cut
289 sub dir_go_low {
290 my ($lbi) = @_;
291 debug( "going low\n" );
292 $lbi->get_curip->dir_go_low;
296 =item dir_go_away( )
298 =cut
299 sub dir_go_away {
300 my ($lbi) = @_;
301 debug( "going away!\n" );
302 $lbi->get_curip->dir_go_away;
306 =item dir_turn_left( )
308 Turning left, like a car (the specs speak about a bicycle, but perl
309 is _so_ fast that we can speak about cars ;) ).
311 =cut
312 sub dir_turn_left {
313 my ($lbi) = @_;
314 debug( "turning on the left\n" );
315 $lbi->get_curip->dir_turn_left;
319 =item dir_turn_right( )
321 Turning right, like a car (the specs speak about a bicycle, but perl
322 is _so_ fast that we can speak about cars ;) ).
324 =cut
325 sub dir_turn_right {
326 my ($lbi) = @_;
327 debug( "turning on the right\n" );
328 $lbi->get_curip->dir_turn_right;
332 =item dir_reverse( )
334 =cut
335 sub dir_reverse {
336 my ($lbi) = @_;
337 debug( "180 deg!\n" );
338 $lbi->get_curip->dir_reverse;
342 =item dir_set_delta( )
344 Hmm, the user seems to know where he wants to go. Let's trust him/her.
346 =cut
347 sub dir_set_delta {
348 my ($lbi) = @_;
349 my $ip = $lbi->get_curip;
350 my ($new_d) = $ip->spop_vec;
351 debug( "setting delta to $new_d\n" );
352 $ip->set_delta( $new_d );
355 =back
359 =head2 Decision making
361 =over 4
363 =item decis_neg( )
365 =cut
366 sub decis_neg {
367 my ($lbi) = @_;
368 my $ip = $lbi->get_curip;
370 # Fetching value.
371 my $val = $ip->spop ? 0 : 1;
372 $ip->spush( $val );
374 debug( "logical not: pushing $val\n" );
378 =item decis_gt( )
380 =cut
381 sub decis_gt {
382 my ($lbi) = @_;
383 my $ip = $lbi->get_curip;
385 # Fetching values.
386 my ($v1, $v2) = $ip->spop_mult(2);
387 debug( "comparing $v1 vs $v2\n" );
388 $ip->spush( ($v1 > $v2) ? 1 : 0 );
392 =item decis_horiz_if( )
394 =cut
395 sub decis_horiz_if {
396 my ($lbi) = @_;
397 my $ip = $lbi->get_curip;
399 # Fetching value.
400 my $val = $ip->spop;
401 $val ? $ip->dir_go_west : $ip->dir_go_east;
402 debug( "horizontal if: going " . ( $val ? "west\n" : "east\n" ) );
406 =item decis_vert_if( )
408 =cut
409 sub decis_vert_if {
410 my ($lbi) = @_;
411 my $ip = $lbi->get_curip;
413 # Fetching value.
414 my $val = $ip->spop;
415 $val ? $ip->dir_go_north : $ip->dir_go_south;
416 debug( "vertical if: going " . ( $val ? "north\n" : "south\n" ) );
420 =item decis_z_if( )
422 =cut
423 sub decis_z_if {
424 my ($lbi) = @_;
425 my $ip = $lbi->get_curip;
427 # Fetching value.
428 my $val = $ip->spop;
429 $val ? $ip->dir_go_low : $ip->dir_go_high;
430 debug( "z if: going " . ( $val ? "low\n" : "high\n" ) );
434 =item decis_cmp( )
436 =cut
437 sub decis_cmp {
438 my ($lbi) = @_;
439 my $ip = $lbi->get_curip;
441 # Fetching value.
442 my ($v1, $v2) = $ip->spop_mult(2);
443 debug( "comparing $v1 with $v2: straight forward!\n"), return if $v1 == $v2;
445 my $dir;
446 if ( $v1 < $v2 ) {
447 $ip->dir_turn_left;
448 $dir = "left";
449 } else {
450 $ip->dir_turn_right;
451 $dir = "right";
453 debug( "comparing $v1 with $v2: turning: $dir\n" );
456 =back
460 =head2 Flow control
462 =over 4
464 =item flow_space( )
466 A serie of spaces is to be treated as B<one> NO-OP.
468 =cut
469 sub flow_space {
470 my ($lbi) = @_;
471 my $ip = $lbi->get_curip;
472 $lbi->_move_ip_till($ip, qr/ /);
473 $lbi->move_ip($lbi->get_curip);
475 my $char = $lbi->get_storage->get_char($ip->get_position);
476 $lbi->_do_instruction($char);
480 =item flow_no_op( )
482 =cut
483 sub flow_no_op {
484 my ($lbi) = @_;
485 debug( "no-op\n" );
489 =item flow_comments( )
491 Bypass comments in B<zero> tick.
493 =cut
494 sub flow_comments {
495 my ($lbi) = @_;
496 my $ip = $lbi->get_curip;
498 $lbi->_move_ip_once($ip); # skip comment ';'
499 $lbi->_move_ip_till( $ip, qr/[^;]/ ); # till just before matching ';'
500 $lbi->_move_ip_once($ip); # till matching ';'
501 $lbi->_move_ip_once($ip); # till just after matching ';'
503 my $char = $lbi->get_storage->get_char($ip->get_position);
504 $lbi->_do_instruction($char);
508 =item flow_trampoline( )
510 =cut
511 sub flow_trampoline {
512 my ($lbi) = @_;
513 $lbi->_move_ip_once($lbi->get_curip);
514 debug( "trampoline! (skipping next instruction)\n" );
518 =item flow_jump_to( )
520 =cut
521 sub flow_jump_to {
522 my ($lbi) = @_;
523 my $ip = $lbi->get_curip;
524 my $count = $ip->spop;
525 debug( "skipping $count instructions\n" );
526 $count == 0 and return;
527 $count < 0 and $ip->dir_reverse; # We can move backward.
528 $lbi->_move_ip_once($lbi->get_curip) for (1..abs($count));
529 $count < 0 and $ip->dir_reverse;
533 =item flow_repeat( )
535 =cut
536 sub flow_repeat {
537 my ($lbi) = @_;
538 my $ip = $lbi->get_curip;
539 my $pos = $ip->get_position;
541 my $kcounter = $ip->spop;
542 debug( "repeating next instruction $kcounter times.\n" );
544 # fetch instruction to repeat
545 $lbi->move_ip($lbi->get_curip);
546 my $char = $lbi->get_storage->get_char($ip->get_position);
548 $char eq 'k' and return; # k cannot be itself repeated
549 $kcounter == 0 and return; # nothing to repeat
550 $kcounter < 0 and return; # oops, error
552 # reset position back to where k is, and repeat instruction
553 $ip->set_position($pos);
554 $lbi->_do_instruction($char) for (1..$kcounter);
558 =item flow_kill_thread( )
560 =cut
561 sub flow_kill_thread {
562 my ($lbi) = @_;
563 debug( "end of Instruction Pointer\n" );
564 $lbi->get_curip->set_end('@');
568 =item flow_quit( )
570 =cut
571 sub flow_quit {
572 my ($lbi) = @_;
573 debug( "end program\n" );
574 $lbi->set_newips( [] );
575 $lbi->set_ips( [] );
576 $lbi->get_curip->set_end('q');
577 $lbi->set_retval( $lbi->get_curip->spop );
580 =back
584 =head2 Stack manipulation
586 =over 4
588 =item stack_pop( )
590 =cut
591 sub stack_pop {
592 my ($lbi) = @_;
593 debug( "popping a value\n" );
594 $lbi->get_curip->spop;
598 =item stack_duplicate( )
600 =cut
601 sub stack_duplicate {
602 my ($lbi) = @_;
603 my $ip = $lbi->get_curip;
604 my $value = $ip->spop;
605 debug( "duplicating value '$value'\n" );
606 $ip->spush( $value );
607 $ip->spush( $value );
611 =item stack_swap( )
613 =cut
614 sub stack_swap {
615 my ($lbi) = @_;
616 my $ ip = $lbi->get_curip;
617 my ($v1, $v2) = $ip->spop_mult(2);
618 debug( "swapping $v1 and $v2\n" );
619 $ip->spush( $v2 );
620 $ip->spush( $v1 );
624 =item stack_clear( )
626 =cut
627 sub stack_clear {
628 my ($lbi) = @_;
629 debug( "clearing stack\n" );
630 $lbi->get_curip->sclear;
633 =back
637 =head2 Stack stack manipulation
639 =over 4
641 =item block_open( )
643 =cut
644 sub block_open {
645 my ($lbi) = @_;
646 my $ip = $lbi->get_curip;
647 debug( "block opening\n" );
649 # Create new TOSS.
650 $ip->ss_create( $ip->spop );
652 # Store current storage offset on SOSS.
653 $ip->soss_push( $ip->get_storage->get_all_components );
655 # Set the new Storage Offset.
656 $lbi->_move_ip_once($lbi->get_curip);
657 $ip->set_storage( $ip->get_position );
658 $ip->dir_reverse;
659 $lbi->_move_ip_once($lbi->get_curip);
660 $ip->dir_reverse;
664 =item block_close( )
666 =cut
667 sub block_close {
668 my ($lbi) = @_;
669 my $ip = $lbi->get_curip;
671 # No opened block.
672 $ip->ss_count <= 0 and $ip->dir_reverse, debug("no opened block\n"), return;
674 debug( "block closing\n" );
676 # Restore Storage offset.
677 $ip->set_storage( $ip->soss_pop_vec );
679 # Remove the TOSS.
680 $ip->ss_remove( $ip->spop );
684 =item bloc_transfer( )
686 =cut
687 sub bloc_transfer {
688 my ($lbi) = @_;
689 my $ip = $lbi->get_curip;
691 $ip->ss_count <= 0 and $ip->dir_reverse, debug("no SOSS available\n"), return;
693 # Transfering values.
694 debug( "transfering values\n" );
695 $ip->ss_transfer( $ip->spop );
698 =back
702 =head2 Funge-space storage
704 =over 4
706 =item store_get( )
708 =cut
709 sub store_get {
710 my ($lbi) = @_;
711 my $ip = $lbi->get_curip;
713 # Fetching coordinates.
714 my ($v) = $ip->spop_vec;
715 $v += $ip->get_storage;
717 # Fetching char.
718 my $val = $lbi->get_storage->get_value( $v );
719 $ip->spush( $val );
721 debug( "fetching value at $v: pushing $val\n" );
725 =item store_put( )
727 =cut
728 sub store_put {
729 my ($lbi) = @_;
730 my $ip = $lbi->get_curip;
732 # Fetching coordinates.
733 my ($v) = $ip->spop_vec;
734 $v += $ip->get_storage;
736 # Fetching char.
737 my $val = $ip->spop;
738 $lbi->get_storage->set_value( $v, $val );
740 debug( "storing value $val at $v\n" );
743 =back
747 =head2 Standard Input/Output
749 =over 4
751 =item stdio_out_num( )
753 =cut
754 sub stdio_out_num {
755 my ($lbi) = @_;
756 my $ip = $lbi->get_curip;
758 # Fetch value and print it.
759 my $val = $ip->spop;
760 debug( "numeric output: $val\n");
761 print( "$val " ) or $ip->dir_reverse;
765 =item stdio_out_ascii( )
767 =cut
768 sub stdio_out_ascii {
769 my ($lbi) = @_;
770 my $ip = $lbi->get_curip;
772 # Fetch value and print it.
773 my $val = $ip->spop;
774 my $chr = chr $val;
775 debug( "ascii output: '$chr' (ord=$val)\n");
776 print( $chr ) or $ip->dir_reverse;
780 =item stdio_in_num( )
782 =cut
783 sub stdio_in_num {
784 my ($lbi) = @_;
785 my $ip = $lbi->get_curip;
786 my ($in, $nb) = ('', 0);
787 my $last = 0;
788 while(!$last) {
789 my $char = $lbi->get_input();
790 $in .= $char if defined $char;
791 my $overflow;
792 ($nb, $overflow) = $in =~ /(-?\d+)(\D*)$/;
793 if((defined($overflow) && length($overflow)) || !defined($char)) {
794 # either we found a non-digit character: $overflow
795 # or else we reached EOF: !$char
796 return $ip->dir_reverse() unless defined $nb;
797 $nb < -2**31 and $nb = -2**31;
798 $nb > 2**31-1 and $nb = 2**31-1;
799 $in = $overflow;
800 $last++;
803 $lbi->set_input( $in );
804 $ip->spush( $nb );
805 debug( "numeric input: pushing $nb\n" );
809 =item stdio_in_ascii( )
811 =cut
812 sub stdio_in_ascii {
813 my ($lbi) = @_;
814 my $ip = $lbi->get_curip;
815 my $in = $lbi->get_input();
816 return $ip->dir_reverse unless defined $in;
817 my $ord = ord $in;
818 $ip->spush( $ord );
819 debug( "ascii input: pushing $ord\n" );
823 =item stdio_in_file( )
825 =cut
826 sub stdio_in_file {
827 my ($lbi) = @_;
828 my $ip = $lbi->get_curip;
830 # Fetch arguments.
831 my $path = $ip->spop_gnirts;
832 my $flag = $ip->spop;
833 my ($vin) = $ip->spop_vec;
834 $vin += $ip->get_storage;
836 # Read file.
837 debug( "input file '$path' at $vin\n" );
838 open F, "<", $path or $ip->dir_reverse, return;
839 my $lines;
841 local $/; # slurp mode.
842 $lines = <F>;
844 close F;
846 # Store the code and the result vector.
847 my ($size) = $flag % 2
848 ? ( $lbi->get_storage->store_binary( $lines, $vin ) )
849 : ( $lbi->get_storage->store( $lines, $vin ) );
850 $ip->spush_vec( $size, $vin );
854 =item stdio_out_file( )
856 =cut
857 sub stdio_out_file {
858 my ($lbi) = @_;
859 my $ip = $lbi->get_curip;
861 # Fetch arguments.
862 my $path = $ip->spop_gnirts;
863 my $flag = $ip->spop;
864 my ($vin) = $ip->spop_vec;
865 $vin += $ip->get_storage;
866 my ($size) = $ip->spop_vec;
867 my $data = $lbi->get_storage->rectangle( $vin, $size );
869 # Cosmetics.
870 my $vend = $vin + $size;
871 debug( "output $vin-$vend to '$path'\n" );
873 # Treat the data chunk as text file?
874 if ( $flag & 0x1 ) {
875 $data =~ s/ +$//mg; # blank lines are now void.
876 $data =~ s/\n+\z/\n/; # final blank lines are stripped.
879 # Write file.
880 open F, ">", $path or $ip->dir_reverse, return;
881 print F $data;
882 close F;
886 =item stdio_sys_exec( )
888 =cut
889 sub stdio_sys_exec {
890 my ($lbi) = @_;
891 my $ip = $lbi->get_curip;
893 # Fetching command.
894 my $path = $ip->spop_gnirts;
895 debug( "spawning external command: $path\n" );
896 system( $path );
897 $ip->spush( $? == -1 ? -1 : $? >> 8 );
900 =back
904 =head2 System info retrieval
906 =over 4
908 =item sys_info( )
910 =cut
911 sub sys_info {
912 my ($lbi) = @_;
913 my $ip = $lbi->get_curip;
914 my $storage = $lbi->get_storage;
916 my $val = $ip->spop;
917 my @infos = ();
919 # 1. flags
920 push @infos, 0x01 # 't' is implemented.
921 | 0x02 # 'i' is implemented.
922 | 0x04 # 'o' is implemented.
923 | 0x08 # '=' is implemented.
924 | !0x10; # buffered IO (non getch).
926 # 2. number of bytes per cell.
927 # 32 bytes Funge: 4 bytes.
928 push @infos, 4;
930 # 3. implementation handprint.
931 my $handprint = 0;
932 $handprint = $handprint * 256 + ord($_) for split //, $lbi->get_handprint;
933 push @infos, $handprint;
935 # 4. version number.
936 my $ver = $Language::Befunge::VERSION;
937 $ver =~ s/\D//g;
938 push @infos, $ver;
940 # 5. ID code for Operating Paradigm.
941 push @infos, 1; # C-language system() call behaviour.
943 # 6. Path separator character.
944 push @infos, ord( catfile('','') );
946 # 7. Number of dimensions.
947 push @infos, $ip->get_dims;
949 # 8. Unique IP number.
950 push @infos, $ip->get_id;
952 # 9. Unique team number for the IP (NetFunge, not implemented).
953 push @infos, 0;
955 # 10. Position of the curent IP.
956 my @pos = ( $ip->get_position->get_all_components );
957 push @infos, \@pos;
959 # 11. Delta of the curent IP.
960 my @delta = ( $ip->get_delta->get_all_components );
961 push @infos, \@delta;
963 # 12. Storage offset of the curent IP.
964 my @stor = ( $ip->get_storage->get_all_components );
965 push @infos, \@stor;
967 # 13. Top-left point.
968 my $min = $storage->min;
969 # FIXME: multiple dims?
970 my @topleft = ( $min->get_component(0), $min->get_component(1) );
971 push @infos, \@topleft;
973 # 14. Dims of the storage.
974 my $max = $storage->max;
975 # FIXME: multiple dims?
976 my @dims = ( $max->get_component(0) - $min->get_component(0),
977 $max->get_component(1) - $min->get_component(1) );
978 push @infos, \@dims;
980 # 15/16. Current date/time.
981 my ($s,$m,$h,$dd,$mm,$yy)=localtime;
982 push @infos, $yy*256*256 + ($mm+1)*256 + $dd;
983 push @infos, $h*256*256 + $m*256 + $s;
985 # 17. Size of stack stack.
986 push @infos, $ip->ss_count + 1;
988 # 18. Size of each stack in the stack stack.
989 # note: the number of stack is given by previous value.
990 my @sizes = reverse $ip->ss_sizes;
991 push @infos, \@sizes;
993 # 19. $file + params.
994 my $str = join chr(0), $lbi->get_file, @{$lbi->get_params}, chr(0)x2;
995 my @cmdline = reverse map { ord } split //, $str;
996 push @infos, \@cmdline;
998 # 20. %ENV
999 # 00EULAV=EMAN0EULAV=EMAN
1000 $str = "";
1001 $str .= "$_=$ENV{$_}".chr(0) foreach sort keys %ENV;
1002 $str .= chr(0);
1003 my @env = reverse map { ord } split //, $str;
1004 push @infos, \@env;
1006 my @cells = map { ref($_) eq 'ARRAY' ? (@$_) : ($_) } reverse @infos;
1008 # Okay, what to do with those cells.
1009 if ( $val <= 0 ) {
1010 # Blindly push them onto the stack.
1011 debug( "system info: pushing the whole stuff\n" );
1012 $ip->spush(@cells);
1014 } elsif ( $val <= scalar(@cells) ) {
1015 # Only push the wanted value.
1016 debug( "system info: pushing the ${val}th value\n" );
1017 $ip->spush( $cells[$#cells-$val+1] );
1019 } else {
1020 # Pick a given value in the stack and push it.
1021 my $offset = $val - $#cells - 1;
1022 my $value = $ip->svalue($offset);
1023 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 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 debug( sprintf("unknown extension $lib (0x%x): reversing\n", $fgrprt) );
1091 $ip->dir_reverse;
1092 } else {
1093 debug( sprintf("extension $lib (0x%x) loaded\n", $fgrprt) );
1094 my $obj = $lib->new;
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 # Checking if library exists.
1129 eval "require $lib";
1130 if ( $@ ) {
1131 debug( sprintf("unknown extension $lib (0x%x): reversing\n", $fgrprt) );
1132 $ip->dir_reverse;
1133 } else {
1134 # Unload the library.
1135 debug( sprintf("unloading library $lib (0x%x)\n", $fgrprt) );
1136 $ip->unload($lib);
1140 =item lib_run_instruction( )
1142 =cut
1144 sub lib_run_instruction {
1145 my ($lbi) = @_;
1146 my $ip = $lbi->get_curip;
1147 my $char = $lbi->get_storage->get_char( $ip->get_position );
1149 # Maybe a library semantics.
1150 debug( "library semantics\n" );
1151 my $stack = $ip->get_libs->{$char};
1153 if ( scalar @$stack ) {
1154 my $obj = $stack->[-1];
1155 debug( "library semantics processed by ".ref($obj)."\n" );
1156 $obj->$char( $lbi );
1157 } else {
1158 # Non-overloaded capitals default to reverse.
1159 debug("no library semantics found: reversing\n");
1160 $ip->dir_reverse;
1164 =back
1166 =cut
1170 __END__
1172 =head1 SEE ALSO
1174 L<Language::Befunge>
1177 =head1 AUTHOR
1179 Jerome Quelin, E<lt>jquelin@cpan.orgE<gt>
1181 Development is discussed on E<lt>language-befunge@mongueurs.netE<gt>
1184 =head1 COPYRIGHT & LICENSE
1186 Copyright (c) 2001-2009 Jerome Quelin, all rights reserved.
1188 This program is free software; you can redistribute it and/or modify
1189 it under the same terms as Perl itself.
1192 =cut