implement comments & spaces, in case we jumped to a point with those chars
[language-befunge.git] / lib / Language / Befunge / Ops.pm
blob79211f123f596f39f4fbbcd11a8893b7ee833421
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 File::Spec::Functions qw{ catfile }; # 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, $char) = @_;
45 # Fetching char.
46 my $ip = $lbi->get_curip;
47 my $num = hex( $char );
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 my $ip = $lbi->get_curip;
471 $lbi->_move_ip_till($ip, qr/ /);
472 $lbi->move_ip($lbi->get_curip);
474 my $char = $lbi->storage->get_char($ip->get_position);
475 $lbi->_do_instruction($char);
479 =item flow_no_op( )
481 =cut
482 sub flow_no_op {
483 my ($lbi) = @_;
484 $lbi->debug( "no-op\n" );
488 =item flow_comments( )
490 Bypass comments in B<zero> tick.
492 =cut
493 sub flow_comments {
494 my ($lbi) = @_;
495 my $ip = $lbi->get_curip;
497 $lbi->_move_ip_once($ip); # skip comment ';'
498 $lbi->_move_ip_till( $ip, qr/[^;]/ ); # till just before matching ';'
499 $lbi->_move_ip_once($ip); # till matching ';'
500 $lbi->_move_ip_once($ip); # till just after matching ';'
502 my $char = $lbi->storage->get_char($ip->get_position);
503 $lbi->_do_instruction($char);
507 =item flow_trampoline( )
509 =cut
510 sub flow_trampoline {
511 my ($lbi) = @_;
512 $lbi->move_ip($lbi->get_curip);
513 $lbi->debug( "trampoline! (skipping next instruction)\n" );
517 =item flow_jump_to( )
519 =cut
520 sub flow_jump_to {
521 my ($lbi) = @_;
522 my $ip = $lbi->get_curip;
523 my $count = $ip->spop;
524 $lbi->debug( "skipping $count instructions\n" );
525 $count == 0 and return;
526 $count < 0 and $ip->dir_reverse; # We can move backward.
527 $lbi->move_ip($lbi->get_curip) for (1..abs($count));
528 $count < 0 and $ip->dir_reverse;
532 =item flow_repeat( )
534 =cut
535 sub flow_repeat {
536 my ($lbi) = @_;
537 my $ip = $lbi->get_curip;
538 my $pos = $ip->get_position;
540 my $kcounter = $ip->spop;
541 $lbi->debug( "repeating next instruction $kcounter times.\n" );
543 # fetch instruction to repeat
544 $lbi->move_ip($lbi->get_curip);
545 my $char = $lbi->storage->get_char($ip->get_position);
547 $char eq 'k' and return; # k cannot be itself repeated
548 $kcounter == 0 and return; # nothing to repeat
549 $kcounter < 0 and return; # oops, error
551 # reset position back to where k is, and repeat instruction
552 $ip->set_position($pos);
553 $lbi->_do_instruction($char) for (1..$kcounter);
557 =item flow_kill_thread( )
559 =cut
560 sub flow_kill_thread {
561 my ($lbi) = @_;
562 $lbi->debug( "end of Instruction Pointer\n" );
563 $lbi->get_curip->set_end('@');
567 =item flow_quit( )
569 =cut
570 sub flow_quit {
571 my ($lbi) = @_;
572 $lbi->debug( "end program\n" );
573 $lbi->set_newips( [] );
574 $lbi->set_ips( [] );
575 $lbi->get_curip->set_end('q');
576 $lbi->set_retval( $lbi->get_curip->spop );
579 =back
583 =head2 Stack manipulation
585 =over 4
587 =item stack_pop( )
589 =cut
590 sub stack_pop {
591 my ($lbi) = @_;
592 $lbi->debug( "popping a value\n" );
593 $lbi->get_curip->spop;
597 =item stack_duplicate( )
599 =cut
600 sub stack_duplicate {
601 my ($lbi) = @_;
602 my $ip = $lbi->get_curip;
603 my $value = $ip->spop;
604 $lbi->debug( "duplicating value '$value'\n" );
605 $ip->spush( $value );
606 $ip->spush( $value );
610 =item stack_swap( )
612 =cut
613 sub stack_swap {
614 my ($lbi) = @_;
615 my $ ip = $lbi->get_curip;
616 my ($v1, $v2) = $ip->spop_mult(2);
617 $lbi->debug( "swapping $v1 and $v2\n" );
618 $ip->spush( $v2 );
619 $ip->spush( $v1 );
623 =item stack_clear( )
625 =cut
626 sub stack_clear {
627 my ($lbi) = @_;
628 $lbi->debug( "clearing stack\n" );
629 $lbi->get_curip->sclear;
632 =back
636 =head2 Stack stack manipulation
638 =over 4
640 =item block_open( )
642 =cut
643 sub block_open {
644 my ($lbi) = @_;
645 my $ip = $lbi->get_curip;
646 $lbi->debug( "block opening\n" );
648 # Create new TOSS.
649 $ip->ss_create( $ip->spop );
651 # Store current storage offset on SOSS.
652 $ip->soss_push( $ip->get_storage->get_all_components );
654 # Set the new Storage Offset.
655 $lbi->move_ip($lbi->get_curip);
656 $ip->set_storage( $ip->get_position );
657 $ip->dir_reverse;
658 $lbi->move_ip($lbi->get_curip);
659 $ip->dir_reverse;
663 =item block_close( )
665 =cut
666 sub block_close {
667 my ($lbi) = @_;
668 my $ip = $lbi->get_curip;
670 # No opened block.
671 $ip->ss_count <= 0 and $ip->dir_reverse, $lbi->debug("no opened block\n"), return;
673 $lbi->debug( "block closing\n" );
675 # Restore Storage offset.
676 $ip->set_storage( $ip->soss_pop_vec );
678 # Remove the TOSS.
679 $ip->ss_remove( $ip->spop );
683 =item bloc_transfer( )
685 =cut
686 sub bloc_transfer {
687 my ($lbi) = @_;
688 my $ip = $lbi->get_curip;
690 $ip->ss_count <= 0 and $ip->dir_reverse, $lbi->debug("no SOSS available\n"), return;
692 # Transfering values.
693 $lbi->debug( "transfering values\n" );
694 $ip->ss_transfer( $ip->spop );
697 =back
701 =head2 Funge-space storage
703 =over 4
705 =item store_get( )
707 =cut
708 sub store_get {
709 my ($lbi) = @_;
710 my $ip = $lbi->get_curip;
712 # Fetching coordinates.
713 my ($v) = $ip->spop_vec;
714 $v += $ip->get_storage;
716 # Fetching char.
717 my $val = $lbi->storage->get_value( $v );
718 $ip->spush( $val );
720 $lbi->debug( "fetching value at $v: pushing $val\n" );
724 =item store_put( )
726 =cut
727 sub store_put {
728 my ($lbi) = @_;
729 my $ip = $lbi->get_curip;
731 # Fetching coordinates.
732 my ($v) = $ip->spop_vec;
733 $v += $ip->get_storage;
735 # Fetching char.
736 my $val = $ip->spop;
737 $lbi->storage->set_value( $v, $val );
739 $lbi->debug( "storing value $val at $v\n" );
742 =back
746 =head2 Standard Input/Output
748 =over 4
750 =item stdio_out_num( )
752 =cut
753 sub stdio_out_num {
754 my ($lbi) = @_;
755 my $ip = $lbi->get_curip;
757 # Fetch value and print it.
758 my $val = $ip->spop;
759 $lbi->debug( "numeric output: $val\n");
760 print( "$val " ) or $ip->dir_reverse;
764 =item stdio_out_ascii( )
766 =cut
767 sub stdio_out_ascii {
768 my ($lbi) = @_;
769 my $ip = $lbi->get_curip;
771 # Fetch value and print it.
772 my $val = $ip->spop;
773 my $chr = chr $val;
774 $lbi->debug( "ascii output: '$chr' (ord=$val)\n");
775 print( $chr ) or $ip->dir_reverse;
779 =item stdio_in_num( )
781 =cut
782 sub stdio_in_num {
783 my ($lbi) = @_;
784 my $ip = $lbi->get_curip;
785 my ($in, $nb) = ('', 0);
786 my $last = 0;
787 while(!$last) {
788 my $char = $lbi->get_input();
789 $in .= $char if defined $char;
790 my $overflow;
791 ($nb, $overflow) = $in =~ /(-?\d+)(\D*)$/;
792 if((defined($overflow) && length($overflow)) || !defined($char)) {
793 # either we found a non-digit character: $overflow
794 # or else we reached EOF: !$char
795 return $ip->dir_reverse() unless defined $nb;
796 $nb < -2**31 and $nb = -2**31;
797 $nb > 2**31-1 and $nb = 2**31-1;
798 $in = $overflow;
799 $last++;
802 $lbi->set_input( $in );
803 $ip->spush( $nb );
804 $lbi->debug( "numeric input: pushing $nb\n" );
808 =item stdio_in_ascii( )
810 =cut
811 sub stdio_in_ascii {
812 my ($lbi) = @_;
813 my $ip = $lbi->get_curip;
814 my $in = $lbi->get_input();
815 return $ip->dir_reverse unless defined $in;
816 my $ord = ord $in;
817 $ip->spush( $ord );
818 $lbi->debug( "ascii input: pushing $ord\n" );
822 =item stdio_in_file( )
824 =cut
825 sub stdio_in_file {
826 my ($lbi) = @_;
827 my $ip = $lbi->get_curip;
829 # Fetch arguments.
830 my $path = $ip->spop_gnirts;
831 my $flag = $ip->spop;
832 my ($vin) = $ip->spop_vec;
833 $vin += $ip->get_storage;
835 # Read file.
836 $lbi->debug( "input file '$path' at $vin\n" );
837 open F, "<", $path or $ip->dir_reverse, return;
838 my $lines;
840 local $/; # slurp mode.
841 $lines = <F>;
843 close F;
845 # Store the code and the result vector.
846 my ($size) = $flag % 2
847 ? ( $lbi->storage->store_binary( $lines, $vin ) )
848 : ( $lbi->storage->store( $lines, $vin ) );
849 $ip->spush_vec( $size, $vin );
853 =item stdio_out_file( )
855 =cut
856 sub stdio_out_file {
857 my ($lbi) = @_;
858 my $ip = $lbi->get_curip;
860 # Fetch arguments.
861 my $path = $ip->spop_gnirts;
862 my $flag = $ip->spop;
863 my ($vin) = $ip->spop_vec;
864 $vin += $ip->get_storage;
865 my ($size) = $ip->spop_vec;
866 my $data = $lbi->storage->rectangle( $vin, $size );
868 # Cosmetics.
869 my $vend = $vin + $size;
870 $lbi->debug( "output $vin-$vend to '$path'\n" );
872 # Treat the data chunk as text file?
873 if ( $flag & 0x1 ) {
874 $data =~ s/ +$//mg; # blank lines are now void.
875 $data =~ s/\n+\z/\n/; # final blank lines are stripped.
878 # Write file.
879 open F, ">", $path or $ip->dir_reverse, return;
880 print F $data;
881 close F;
885 =item stdio_sys_exec( )
887 =cut
888 sub stdio_sys_exec {
889 my ($lbi) = @_;
890 my $ip = $lbi->get_curip;
892 # Fetching command.
893 my $path = $ip->spop_gnirts;
894 $lbi->debug( "spawning external command: $path\n" );
895 system( $path );
896 $ip->spush( $? == -1 ? -1 : $? >> 8 );
899 =back
903 =head2 System info retrieval
905 =over 4
907 =item sys_info( )
909 =cut
910 sub sys_info {
911 my ($lbi) = @_;
912 my $ip = $lbi->get_curip;
913 my $storage = $lbi->storage;
915 my $val = $ip->spop;
916 my @infos = ();
918 # 1. flags
919 push @infos, 0x01 # 't' is implemented.
920 | 0x02 # 'i' is implemented.
921 | 0x04 # 'o' is implemented.
922 | 0x08 # '=' is implemented.
923 | !0x10; # buffered IO (non getch).
925 # 2. number of bytes per cell.
926 # 32 bytes Funge: 4 bytes.
927 push @infos, 4;
929 # 3. implementation handprint.
930 my $handprint = 0;
931 $handprint = $handprint * 256 + ord($_) for split //, $lbi->get_handprint;
932 push @infos, $handprint;
934 # 4. version number.
935 my $ver = $Language::Befunge::VERSION;
936 $ver =~ s/\D//g;
937 push @infos, $ver;
939 # 5. ID code for Operating Paradigm.
940 push @infos, 1; # C-language system() call behaviour.
942 # 6. Path separator character.
943 push @infos, ord( catfile('','') );
945 # 7. Number of dimensions.
946 push @infos, $ip->get_dims;
948 # 8. Unique IP number.
949 push @infos, $ip->get_id;
951 # 9. Unique team number for the IP (NetFunge, not implemented).
952 push @infos, 0;
954 # 10. Position of the curent IP.
955 my @pos = ( $ip->get_position->get_all_components );
956 push @infos, \@pos;
958 # 11. Delta of the curent IP.
959 my @delta = ( $ip->get_delta->get_all_components );
960 push @infos, \@delta;
962 # 12. Storage offset of the curent IP.
963 my @stor = ( $ip->get_storage->get_all_components );
964 push @infos, \@stor;
966 # 13. Top-left point.
967 my $min = $storage->min;
968 # FIXME: multiple dims?
969 my @topleft = ( $min->get_component(0), $min->get_component(1) );
970 push @infos, \@topleft;
972 # 14. Dims of the storage.
973 my $max = $storage->max;
974 # FIXME: multiple dims?
975 my @dims = ( $max->get_component(0) - $min->get_component(0),
976 $max->get_component(1) - $min->get_component(1) );
977 push @infos, \@dims;
979 # 15/16. Current date/time.
980 my ($s,$m,$h,$dd,$mm,$yy)=localtime;
981 push @infos, $yy*256*256 + $mm*256 + $dd;
982 push @infos, $h*256*256 + $m*256 + $s;
984 # 17. Size of stack stack.
985 push @infos, $ip->ss_count + 1;
987 # 18. Size of each stack in the stack stack.
988 # !!FIXME!! Funge specs just tell to push onto the
989 # stack the size of the stacks, but nothing is
990 # said about how user will retrieve the number of
991 # stacks.
992 my @sizes = reverse $ip->ss_sizes;
993 push @infos, \@sizes;
995 # 19. $file + params.
996 my $str = join chr(0), $lbi->get_file, @{$lbi->get_params}, chr(0)x2;
997 my @cmdline = reverse map { ord } split //, $str;
998 push @infos, \@cmdline;
1000 # 20. %ENV
1001 # 00EULAV=EMAN0EULAV=EMAN
1002 $str = "";
1003 $str .= "$_=$ENV{$_}".chr(0) foreach sort keys %ENV;
1004 $str .= chr(0);
1005 my @env = reverse map { ord } split //, $str;
1006 push @infos, \@env;
1008 my @cells = map { ref($_) eq 'ARRAY' ? (@$_) : ($_) } reverse @infos;
1010 # Okay, what to do with those cells.
1011 if ( $val <= 0 ) {
1012 # Blindly push them onto the stack.
1013 $lbi->debug( "system info: pushing the whole stuff\n" );
1014 $ip->spush(@cells);
1016 } elsif ( $val <= scalar(@cells) ) {
1017 # Only push the wanted value.
1018 $lbi->debug( "system info: pushing the ${val}th value\n" );
1019 $ip->spush( $cells[$#cells-$val+1] );
1021 } else {
1022 # Pick a given value in the stack and push it.
1023 my $offset = $val - $#cells - 1;
1024 my $value = $ip->svalue($offset);
1025 $lbi->debug( "system info: picking the ${offset}th value from the stack = $value\n" );
1026 $ip->spush( $value );
1030 =back
1034 =head2 Concurrent Funge
1036 =over 4
1038 =item spawn_ip( )
1040 =cut
1041 sub spawn_ip {
1042 my ($lbi) = @_;
1044 # Cosmetics.
1045 $lbi->debug( "spawning new IP\n" );
1047 # Cloning and storing new IP.
1048 my $newip = $lbi->get_curip->clone;
1049 $newip->dir_reverse;
1050 $lbi->move_ip($newip);
1051 push @{ $lbi->get_newips }, $newip;
1054 =back
1058 =head2 Library semantics
1060 =over 4
1062 =item lib_load( )
1064 =cut
1065 sub lib_load {
1066 my ($lbi) = @_;
1067 my $ip = $lbi->get_curip;
1069 # Fetching fingerprint.
1070 my $count = $ip->spop;
1071 my $fgrprt = 0;
1072 while ( $count-- > 0 ) {
1073 my $val = $ip->spop;
1074 $lbi->abort( "Attempt to build a fingerprint with a negative number" )
1075 if $val < 0;
1076 $fgrprt = $fgrprt * 256 + $val;
1079 # Transform the fingerprint into a library name.
1080 my $lib = "";
1081 my $finger = $fgrprt;
1082 while ( $finger > 0 ) {
1083 my $c = $finger % 0x100;
1084 $lib .= chr($c);
1085 $finger = int ( $finger / 0x100 );
1087 $lib = "Language::Befunge::lib::" . reverse $lib;
1089 # Checking if library exists.
1090 eval "require $lib";
1091 if ( $@ ) {
1092 $lbi->debug( sprintf("unknown extension $lib (0x%x): reversing\n", $fgrprt) );
1093 $ip->dir_reverse;
1094 } else {
1095 $lbi->debug( sprintf("extension $lib (0x%x) loaded\n", $fgrprt) );
1096 my $obj = new $lib;
1097 $ip->load( $obj );
1098 $ip->spush( $fgrprt, 1 );
1103 =item lib_unload( )
1105 =cut
1106 sub lib_unload {
1107 my ($lbi) = @_;
1108 my $ip = $lbi->get_curip;
1110 # Fetching fingerprint.
1111 my $count = $ip->spop;
1112 my $fgrprt = 0;
1113 while ( $count-- > 0 ) {
1114 my $val = $ip->spop;
1115 $lbi->abort( "Attempt to build a fingerprint with a negative number" )
1116 if $val < 0;
1117 $fgrprt = $fgrprt * 256 + $val;
1120 # Transform the fingerprint into a library name.
1121 my $lib = "";
1122 my $finger = $fgrprt;
1123 while ( $finger > 0 ) {
1124 my $c = $finger % 0x100;
1125 $lib .= chr($c);
1126 $finger = int ( $finger / 0x100 );
1128 $lib = "Language::Befunge::lib::" . reverse $lib;
1130 # Unload the library.
1131 if ( defined( $ip->unload($lib) ) ) {
1132 $lbi->debug( sprintf("unloading library $lib (0x%x)\n", $fgrprt) );
1133 } else {
1134 # The library wasn't loaded.
1135 $lbi->debug( sprintf("library $lib (0x%x) wasn't loaded\n", $fgrprt) );
1136 $ip->dir_reverse;
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->storage->get_char( $ip->get_position );
1149 # Maybe a library semantics.
1150 $lbi->debug( "library semantics\n" );
1152 foreach my $obj ( @{ $ip->get_libs } ) {
1153 # Try the loaded libraries in order.
1154 eval "\$obj->$char(\$lbi)";
1155 if( $@ ) {
1156 $lbi->debug( ref($obj) . "->$char failed: $@" );
1157 next;
1160 # We manage to get a library.
1161 $lbi->debug( "library semantics processed by ".ref($obj)."\n" );
1162 return;
1165 # Non-overloaded capitals default to reverse.
1166 $lbi->debug("no library semantics found: reversing\n");
1167 $ip->dir_reverse;
1170 =back
1172 =cut
1176 __END__
1178 =head1 SEE ALSO
1180 L<Language::Befunge>
1183 =head1 AUTHOR
1185 Jerome Quelin, E<lt>jquelin@cpan.orgE<gt>
1187 Development is discussed on E<lt>language-befunge@mongueurs.netE<gt>
1190 =head1 COPYRIGHT & LICENSE
1192 Copyright (c) 2001-2008 Jerome Quelin, all rights reserved.
1194 This program is free software; you can redistribute it and/or modify
1195 it under the same terms as Perl itself.
1198 =cut