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
;
16 use File
::Spec
::Functions
qw{ catfile
}; # For the 'y' instruction.
21 Language::Befunge::Ops - definition of the various operations
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.
37 =item num_push_number( )
39 Push the current number onto the TOSS.
43 my ($lbi, $char) = @_;
46 my $ip = $lbi->get_curip;
47 my $num = hex( $char );
53 $lbi->debug( "pushing number '$num'\n" );
64 =item str_enter_string_mode( )
67 sub str_enter_string_mode
{
71 $lbi->debug( "entering string mode\n" );
73 # Entering string-mode.
74 $lbi->get_curip->set_string_mode(1);
78 =item str_fetch_char( )
83 my $ip = $lbi->get_curip;
86 $lbi->_move_ip_once($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 );
94 $lbi->debug( "pushing value $ord (char='$chr')\n" );
98 =item str_store_char( )
103 my $ip = $lbi->get_curip;
106 $lbi->move_ip($lbi->get_curip);
112 $lbi->storage->set_value( $ip->get_position, $val );
113 my $chr = $lbi->storage->get_char( $ip->get_position );
116 $lbi->debug( "storing value $val (char='$chr')\n" );
123 =head2 Mathematical operations
127 =item math_addition( )
132 my $ip = $lbi->get_curip;
135 my ($v1, $v2) = $ip->spop_mult(2);
136 $lbi->debug( "adding: $v1+$v2\n" );
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" );
148 =item math_substraction( )
151 sub math_substraction
{
153 my $ip = $lbi->get_curip;
156 my ($v1, $v2) = $ip->spop_mult(2);
157 $lbi->debug( "substracting: $v1-$v2\n" );
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" );
169 =item math_multiplication( )
172 sub math_multiplication
{
174 my $ip = $lbi->get_curip;
177 my ($v1, $v2) = $ip->spop_mult(2);
178 $lbi->debug( "multiplicating: $v1*$v2\n" );
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" );
190 =item math_division( )
195 my $ip = $lbi->get_curip;
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.
209 =item math_remainder( )
214 my $ip = $lbi->get_curip;
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.
231 =head2 Direction changing
240 $lbi->debug( "going east\n" );
241 $lbi->get_curip->dir_go_east;
250 $lbi->debug( "going west\n" );
251 $lbi->get_curip->dir_go_west;
255 =item dir_go_north( )
260 $lbi->debug( "going north\n" );
261 $lbi->get_curip->dir_go_north;
265 =item dir_go_south( )
270 $lbi->debug( "going south\n" );
271 $lbi->get_curip->dir_go_south;
280 $lbi->debug( "going high\n" );
281 $lbi->get_curip->dir_go_high;
290 $lbi->debug( "going low\n" );
291 $lbi->get_curip->dir_go_low;
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 ;) ).
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 ;) ).
326 $lbi->debug( "turning on the right\n" );
327 $lbi->get_curip->dir_turn_right;
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.
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 );
358 =head2 Decision making
367 my $ip = $lbi->get_curip;
370 my $val = $ip->spop ?
0 : 1;
373 $lbi->debug( "logical not: pushing $val\n" );
382 my $ip = $lbi->get_curip;
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( )
396 my $ip = $lbi->get_curip;
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( )
410 my $ip = $lbi->get_curip;
414 $val ?
$ip->dir_go_north : $ip->dir_go_south;
415 $lbi->debug( "vertical if: going " . ( $val ?
"north\n" : "south\n" ) );
424 my $ip = $lbi->get_curip;
428 $val ?
$ip->dir_go_low : $ip->dir_go_high;
429 $lbi->debug( "z if: going " . ( $val ?
"low\n" : "high\n" ) );
438 my $ip = $lbi->get_curip;
441 my ($v1, $v2) = $ip->spop_mult(2);
442 $lbi->debug( "comparing $v1 with $v2: straight forward!\n"), return if $v1 == $v2;
452 $lbi->debug( "comparing $v1 with $v2: turning: $dir\n" );
465 A serie of spaces is to be treated as B<one> NO-OP.
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);
484 $lbi->debug( "no-op\n" );
488 =item flow_comments( )
490 Bypass comments in B<zero> tick.
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( )
510 sub flow_trampoline
{
512 $lbi->move_ip($lbi->get_curip);
513 $lbi->debug( "trampoline! (skipping next instruction)\n" );
517 =item flow_jump_to( )
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;
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( )
560 sub flow_kill_thread
{
562 $lbi->debug( "end of Instruction Pointer\n" );
563 $lbi->get_curip->set_end('@');
572 $lbi->debug( "end program\n" );
573 $lbi->set_newips( [] );
575 $lbi->get_curip->set_end('q');
576 $lbi->set_retval( $lbi->get_curip->spop );
583 =head2 Stack manipulation
592 $lbi->debug( "popping a value\n" );
593 $lbi->get_curip->spop;
597 =item stack_duplicate( )
600 sub stack_duplicate
{
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 );
615 my $ ip
= $lbi->get_curip;
616 my ($v1, $v2) = $ip->spop_mult(2);
617 $lbi->debug( "swapping $v1 and $v2\n" );
628 $lbi->debug( "clearing stack\n" );
629 $lbi->get_curip->sclear;
636 =head2 Stack stack manipulation
645 my $ip = $lbi->get_curip;
646 $lbi->debug( "block opening\n" );
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 );
658 $lbi->move_ip($lbi->get_curip);
668 my $ip = $lbi->get_curip;
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 );
679 $ip->ss_remove( $ip->spop );
683 =item bloc_transfer( )
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 );
701 =head2 Funge-space storage
710 my $ip = $lbi->get_curip;
712 # Fetching coordinates.
713 my ($v) = $ip->spop_vec;
714 $v += $ip->get_storage;
717 my $val = $lbi->storage->get_value( $v );
720 $lbi->debug( "fetching value at $v: pushing $val\n" );
729 my $ip = $lbi->get_curip;
731 # Fetching coordinates.
732 my ($v) = $ip->spop_vec;
733 $v += $ip->get_storage;
737 $lbi->storage->set_value( $v, $val );
739 $lbi->debug( "storing value $val at $v\n" );
746 =head2 Standard Input/Output
750 =item stdio_out_num( )
755 my $ip = $lbi->get_curip;
757 # Fetch value and print it.
759 $lbi->debug( "numeric output: $val\n");
760 print( "$val " ) or $ip->dir_reverse;
764 =item stdio_out_ascii( )
767 sub stdio_out_ascii
{
769 my $ip = $lbi->get_curip;
771 # Fetch value and print it.
774 $lbi->debug( "ascii output: '$chr' (ord=$val)\n");
775 print( $chr ) or $ip->dir_reverse;
779 =item stdio_in_num( )
784 my $ip = $lbi->get_curip;
785 my ($in, $nb) = ('', 0);
788 my $char = $lbi->get_input();
789 $in .= $char if defined $char;
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;
802 $lbi->set_input( $in );
804 $lbi->debug( "numeric input: pushing $nb\n" );
808 =item stdio_in_ascii( )
813 my $ip = $lbi->get_curip;
814 my $in = $lbi->get_input();
815 return $ip->dir_reverse unless defined $in;
818 $lbi->debug( "ascii input: pushing $ord\n" );
822 =item stdio_in_file( )
827 my $ip = $lbi->get_curip;
830 my $path = $ip->spop_gnirts;
831 my $flag = $ip->spop;
832 my ($vin) = $ip->spop_vec;
833 $vin += $ip->get_storage;
836 $lbi->debug( "input file '$path' at $vin\n" );
837 open F
, "<", $path or $ip->dir_reverse, return;
840 local $/; # slurp mode.
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( )
858 my $ip = $lbi->get_curip;
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 );
869 my $vend = $vin + $size;
870 $lbi->debug( "output $vin-$vend to '$path'\n" );
872 # Treat the data chunk as text file?
874 $data =~ s/ +$//mg; # blank lines are now void.
875 $data =~ s/\n+\z/\n/; # final blank lines are stripped.
879 open F
, ">", $path or $ip->dir_reverse, return;
885 =item stdio_sys_exec( )
890 my $ip = $lbi->get_curip;
893 my $path = $ip->spop_gnirts;
894 $lbi->debug( "spawning external command: $path\n" );
896 $ip->spush( $?
== -1 ?
-1 : $?
>> 8 );
903 =head2 System info retrieval
912 my $ip = $lbi->get_curip;
913 my $storage = $lbi->storage;
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.
929 # 3. implementation handprint.
931 $handprint = $handprint * 256 + ord($_) for split //, $lbi->get_handprint;
932 push @infos, $handprint;
935 my $ver = $Language::Befunge
::VERSION
;
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).
954 # 10. Position of the curent IP.
955 my @pos = ( $ip->get_position->get_all_components );
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 );
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) );
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
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;
1001 # 00EULAV=EMAN0EULAV=EMAN
1003 $str .= "$_=$ENV{$_}".chr(0) foreach sort keys %ENV;
1005 my @env = reverse map { ord } split //, $str;
1008 my @cells = map { ref($_) eq 'ARRAY' ?
(@
$_) : ($_) } reverse @infos;
1010 # Okay, what to do with those cells.
1012 # Blindly push them onto the stack.
1013 $lbi->debug( "system info: pushing the whole stuff\n" );
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] );
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 );
1034 =head2 Concurrent Funge
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;
1058 =head2 Library semantics
1067 my $ip = $lbi->get_curip;
1069 # Fetching fingerprint.
1070 my $count = $ip->spop;
1072 while ( $count-- > 0 ) {
1073 my $val = $ip->spop;
1074 $lbi->abort( "Attempt to build a fingerprint with a negative number" )
1076 $fgrprt = $fgrprt * 256 + $val;
1079 # Transform the fingerprint into a library name.
1081 my $finger = $fgrprt;
1082 while ( $finger > 0 ) {
1083 my $c = $finger % 0x100;
1085 $finger = int ( $finger / 0x100 );
1087 $lib = "Language::Befunge::lib::" . reverse $lib;
1089 # Checking if library exists.
1090 eval "require $lib";
1092 $lbi->debug( sprintf("unknown extension $lib (0x%x): reversing\n", $fgrprt) );
1095 $lbi->debug( sprintf("extension $lib (0x%x) loaded\n", $fgrprt) );
1098 $ip->spush( $fgrprt, 1 );
1108 my $ip = $lbi->get_curip;
1110 # Fetching fingerprint.
1111 my $count = $ip->spop;
1113 while ( $count-- > 0 ) {
1114 my $val = $ip->spop;
1115 $lbi->abort( "Attempt to build a fingerprint with a negative number" )
1117 $fgrprt = $fgrprt * 256 + $val;
1120 # Transform the fingerprint into a library name.
1122 my $finger = $fgrprt;
1123 while ( $finger > 0 ) {
1124 my $c = $finger % 0x100;
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) );
1134 # The library wasn't loaded.
1135 $lbi->debug( sprintf("library $lib (0x%x) wasn't loaded\n", $fgrprt) );
1140 =item lib_run_instruction( )
1144 sub lib_run_instruction
{
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)";
1156 $lbi->debug( ref($obj) . "->$char failed: $@" );
1160 # We manage to get a library.
1161 $lbi->debug( "library semantics processed by ".ref($obj)."\n" );
1165 # Non-overloaded capitals default to reverse.
1166 $lbi->debug("no library semantics found: reversing\n");
1180 L<Language::Befunge>
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.