fix path separator: $Config{path_sep} separates things in $PATH!
[language-befunge.git] / lib / Language / Befunge / Ops.pm
blobdc2e0037a2c0a1cc1dfbfb0adee1d1d373497764
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) = @_;
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 $count < 0 and $ip->dir_reverse;
523 =item flow_repeat( )
525 =cut
526 sub flow_repeat {
527 my ($lbi) = @_;
528 my $ip = $lbi->get_curip;
530 my $kcounter = $ip->spop;
531 $lbi->debug( "repeating next instruction $kcounter times.\n" );
532 $lbi->move_ip($lbi->get_curip);
534 # Nothing to repeat.
535 $kcounter == 0 and return;
537 # Ooops, error.
538 $kcounter < 0 and $lbi->abort( "Attempt to repeat ('k') a negative number of times ($kcounter)" );
540 # Fetch instruction to repeat.
541 my $val = $lbi->storage->get_value( $ip->get_position );
543 # Check if we can repeat the instruction.
544 $val > 0 and $val < 256 and chr($val) =~ /([ ;k])/ and return;
546 $lbi->process_ip(0) for (1..$kcounter);
550 =item flow_kill_thread( )
552 =cut
553 sub flow_kill_thread {
554 my ($lbi) = @_;
555 $lbi->debug( "end of Instruction Pointer\n" );
556 $lbi->get_curip->set_end('@');
560 =item flow_quit( )
562 =cut
563 sub flow_quit {
564 my ($lbi) = @_;
565 $lbi->debug( "end program\n" );
566 $lbi->set_newips( [] );
567 $lbi->set_ips( [] );
568 $lbi->get_curip->set_end('q');
569 $lbi->set_retval( $lbi->get_curip->spop );
572 =back
576 =head2 Stack manipulation
578 =over 4
580 =item stack_pop( )
582 =cut
583 sub stack_pop {
584 my ($lbi) = @_;
585 $lbi->debug( "popping a value\n" );
586 $lbi->get_curip->spop;
590 =item stack_duplicate( )
592 =cut
593 sub stack_duplicate {
594 my ($lbi) = @_;
595 my $ip = $lbi->get_curip;
596 my $value = $ip->spop;
597 $lbi->debug( "duplicating value '$value'\n" );
598 $ip->spush( $value );
599 $ip->spush( $value );
603 =item stack_swap( )
605 =cut
606 sub stack_swap {
607 my ($lbi) = @_;
608 my $ ip = $lbi->get_curip;
609 my ($v1, $v2) = $ip->spop_mult(2);
610 $lbi->debug( "swapping $v1 and $v2\n" );
611 $ip->spush( $v2 );
612 $ip->spush( $v1 );
616 =item stack_clear( )
618 =cut
619 sub stack_clear {
620 my ($lbi) = @_;
621 $lbi->debug( "clearing stack\n" );
622 $lbi->get_curip->sclear;
625 =back
629 =head2 Stack stack manipulation
631 =over 4
633 =item block_open( )
635 =cut
636 sub block_open {
637 my ($lbi) = @_;
638 my $ip = $lbi->get_curip;
639 $lbi->debug( "block opening\n" );
641 # Create new TOSS.
642 $ip->ss_create( $ip->spop );
644 # Store current storage offset on SOSS.
645 $ip->soss_push( $ip->get_storage->get_all_components );
647 # Set the new Storage Offset.
648 $lbi->move_ip($lbi->get_curip);
649 $ip->set_storage( $ip->get_position );
650 $ip->dir_reverse;
651 $lbi->move_ip($lbi->get_curip);
652 $ip->dir_reverse;
656 =item block_close( )
658 =cut
659 sub block_close {
660 my ($lbi) = @_;
661 my $ip = $lbi->get_curip;
663 # No opened block.
664 $ip->ss_count <= 0 and $ip->dir_reverse, $lbi->debug("no opened block\n"), return;
666 $lbi->debug( "block closing\n" );
668 # Restore Storage offset.
669 $ip->set_storage( $ip->soss_pop_vec );
671 # Remove the TOSS.
672 $ip->ss_remove( $ip->spop );
676 =item bloc_transfer( )
678 =cut
679 sub bloc_transfer {
680 my ($lbi) = @_;
681 my $ip = $lbi->get_curip;
683 $ip->ss_count <= 0 and $ip->dir_reverse, $lbi->debug("no SOSS available\n"), return;
685 # Transfering values.
686 $lbi->debug( "transfering values\n" );
687 $ip->ss_transfer( $ip->spop );
690 =back
694 =head2 Funge-space storage
696 =over 4
698 =item store_get( )
700 =cut
701 sub store_get {
702 my ($lbi) = @_;
703 my $ip = $lbi->get_curip;
705 # Fetching coordinates.
706 my ($v) = $ip->spop_vec;
707 $v += $ip->get_storage;
709 # Fetching char.
710 my $val = $lbi->storage->get_value( $v );
711 $ip->spush( $val );
713 $lbi->debug( "fetching value at $v: pushing $val\n" );
717 =item store_put( )
719 =cut
720 sub store_put {
721 my ($lbi) = @_;
722 my $ip = $lbi->get_curip;
724 # Fetching coordinates.
725 my ($v) = $ip->spop_vec;
726 $v += $ip->get_storage;
728 # Fetching char.
729 my $val = $ip->spop;
730 $lbi->storage->set_value( $v, $val );
732 $lbi->debug( "storing value $val at $v\n" );
735 =back
739 =head2 Standard Input/Output
741 =over 4
743 =item stdio_out_num( )
745 =cut
746 sub stdio_out_num {
747 my ($lbi) = @_;
748 my $ip = $lbi->get_curip;
750 # Fetch value and print it.
751 my $val = $ip->spop;
752 $lbi->debug( "numeric output: $val\n");
753 print( "$val " ) or $ip->dir_reverse;
757 =item stdio_out_ascii( )
759 =cut
760 sub stdio_out_ascii {
761 my ($lbi) = @_;
762 my $ip = $lbi->get_curip;
764 # Fetch value and print it.
765 my $val = $ip->spop;
766 my $chr = chr $val;
767 $lbi->debug( "ascii output: '$chr' (ord=$val)\n");
768 print( $chr ) or $ip->dir_reverse;
772 =item stdio_in_num( )
774 =cut
775 sub stdio_in_num {
776 my ($lbi) = @_;
777 my $ip = $lbi->get_curip;
778 my ($in, $nb) = ('', 0);
779 my $last = 0;
780 while(!$last) {
781 my $char = $lbi->get_input();
782 $in .= $char if defined $char;
783 my $overflow;
784 ($nb, $overflow) = $in =~ /(-?\d+)(\D*)$/;
785 if((defined($overflow) && length($overflow)) || !defined($char)) {
786 # either we found a non-digit character: $overflow
787 # or else we reached EOF: !$char
788 return $ip->dir_reverse() unless defined $nb;
789 $nb < -2**31 and $nb = -2**31;
790 $nb > 2**31-1 and $nb = 2**31-1;
791 $in = $overflow;
792 $last++;
795 $lbi->set_input( $in );
796 $ip->spush( $nb );
797 $lbi->debug( "numeric input: pushing $nb\n" );
801 =item stdio_in_ascii( )
803 =cut
804 sub stdio_in_ascii {
805 my ($lbi) = @_;
806 my $ip = $lbi->get_curip;
807 my $in = $lbi->get_input();
808 return $ip->dir_reverse unless defined $in;
809 my $ord = ord $in;
810 $ip->spush( $ord );
811 $lbi->debug( "ascii input: pushing $ord\n" );
815 =item stdio_in_file( )
817 =cut
818 sub stdio_in_file {
819 my ($lbi) = @_;
820 my $ip = $lbi->get_curip;
822 # Fetch arguments.
823 my $path = $ip->spop_gnirts;
824 my $flag = $ip->spop;
825 my ($vin) = $ip->spop_vec;
826 $vin += $ip->get_storage;
828 # Read file.
829 $lbi->debug( "input file '$path' at $vin\n" );
830 open F, "<", $path or $ip->dir_reverse, return;
831 my $lines;
833 local $/; # slurp mode.
834 $lines = <F>;
836 close F;
838 # Store the code and the result vector.
839 my ($size) = $flag % 2
840 ? ( $lbi->storage->store_binary( $lines, $vin ) )
841 : ( $lbi->storage->store( $lines, $vin ) );
842 $ip->spush_vec( $size, $vin );
846 =item stdio_out_file( )
848 =cut
849 sub stdio_out_file {
850 my ($lbi) = @_;
851 my $ip = $lbi->get_curip;
853 # Fetch arguments.
854 my $path = $ip->spop_gnirts;
855 my $flag = $ip->spop;
856 my ($vin) = $ip->spop_vec;
857 $vin += $ip->get_storage;
858 my ($size) = $ip->spop_vec;
859 my $data = $lbi->storage->rectangle( $vin, $size );
861 # Cosmetics.
862 my $vend = $vin + $size;
863 $lbi->debug( "output $vin-$vend to '$path'\n" );
865 # Treat the data chunk as text file?
866 if ( $flag & 0x1 ) {
867 $data =~ s/ +$//mg; # blank lines are now void.
868 $data =~ s/\n+\z/\n/; # final blank lines are stripped.
871 # Write file.
872 open F, ">", $path or $ip->dir_reverse, return;
873 print F $data;
874 close F;
878 =item stdio_sys_exec( )
880 =cut
881 sub stdio_sys_exec {
882 my ($lbi) = @_;
883 my $ip = $lbi->get_curip;
885 # Fetching command.
886 my $path = $ip->spop_gnirts;
887 $lbi->debug( "spawning external command: $path\n" );
888 system( $path );
889 $ip->spush( $? == -1 ? -1 : $? >> 8 );
892 =back
896 =head2 System info retrieval
898 =over 4
900 =item sys_info( )
902 =cut
903 sub sys_info {
904 my ($lbi) = @_;
905 my $ip = $lbi->get_curip;
906 my $storage = $lbi->storage;
908 my $val = $ip->spop;
909 my @cells = ();
911 # 1. flags
912 push @cells, 0x01 # 't' is implemented.
913 | 0x02 # 'i' is implemented.
914 | 0x04 # 'o' is implemented.
915 | 0x08 # '=' is implemented.
916 | !0x10; # buffered IO (non getch).
918 # 2. number of bytes per cell.
919 # 32 bytes Funge: 4 bytes.
920 push @cells, 4;
922 # 3. implementation handprint.
923 my $handprint = 0;
924 $handprint = $handprint * 256 + ord($_) for split //, $lbi->get_handprint;
925 push @cells, $handprint;
927 # 4. version number.
928 my $ver = $Language::Befunge::VERSION;
929 $ver =~ s/\D//g;
930 push @cells, $ver;
932 # 5. ID code for Operating Paradigm.
933 push @cells, 1; # C-language system() call behaviour.
935 # 6. Path separator character.
936 push @cells, ord( catfile('','') );
938 # 7. Number of dimensions.
939 push @cells, $ip->get_dims;
941 # 8. Unique IP number.
942 push @cells, $ip->get_id;
944 # 9. Unique team number for the IP (NetFunge, not implemented).
945 push @cells, 0;
947 # 10. Position of the curent IP.
948 my @pos = ( $ip->get_position->get_all_components );
949 push @cells, \@pos;
951 # 11. Delta of the curent IP.
952 my @delta = ( $ip->get_delta->get_all_components );
953 push @cells, \@delta;
955 # 12. Storage offset of the curent IP.
956 my @stor = ( $ip->get_storage->get_all_components );
957 push @cells, \@stor;
959 # 13. Top-left point.
960 my $min = $storage->min;
961 # FIXME: multiple dims?
962 my @topleft = ( $min->get_component(0), $min->get_component(1) );
963 push @cells, \@topleft;
965 # 14. Dims of the storage.
966 my $max = $storage->max;
967 # FIXME: multiple dims?
968 my @dims = ( $max->get_component(0) - $min->get_component(0) + 1,
969 $max->get_component(1) - $min->get_component(1) + 1 );
970 push @cells, \@dims;
972 # 15/16. Current date/time.
973 my ($s,$m,$h,$dd,$mm,$yy)=localtime;
974 push @cells, $yy*256*256 + $mm*256 + $dd;
975 push @cells, $h*256*256 + $m*256 + $s;
977 # 17. Size of stack stack.
978 push @cells, $ip->ss_count + 1;
980 # 18. Size of each stack in the stack stack.
981 # !!FIXME!! Funge specs just tell to push onto the
982 # stack the size of the stacks, but nothing is
983 # said about how user will retrieve the number of
984 # stacks.
985 my @sizes = reverse $ip->ss_sizes;
986 push @cells, \@sizes;
988 # 19. $file + params.
989 my $str = join chr(0), $lbi->get_file, @{$lbi->get_params}, chr(0);
990 my @cmdline = reverse map { ord } split //, $str;
991 push @cells, \@cmdline;
993 # 20. %ENV
994 # 00EULAV=EMAN0EULAV=EMAN
995 $str = "";
996 $str .= "$_=$ENV{$_}".chr(0) foreach sort keys %ENV;
997 $str .= chr(0);
998 my @env = reverse map { ord } split //, $str;
999 push @cells, \@env;
1001 # Okay, what to do with those cells.
1002 if ( $val <= 0 ) {
1003 # Blindly push them onto the stack.
1004 $lbi->debug( "system info: pushing the whole stuff\n" );
1005 foreach my $cell ( reverse @cells ) {
1006 $ip->spush( ref( $cell ) eq "ARRAY" ?
1007 @$cell : $cell );
1010 } elsif ( $val <= 20 ) {
1011 # Only push the wanted value.
1012 $lbi->debug( "system info: pushing the ${val}th value\n" );
1013 $ip->spush( ref( $cells[$val-1] ) eq "ARRAY" ?
1014 @{ $cells[$val-1] } : $cells[$val-1] );
1016 } else {
1017 # Pick a given value in the stack and push it.
1018 my $offset = $val - 20;
1019 my $value = $ip->svalue($offset);
1020 $lbi->debug( "system info: picking the ${offset}th value from the stack = $value\n" );
1021 $ip->spush( $value );
1025 =back
1029 =head2 Concurrent Funge
1031 =over 4
1033 =item spawn_ip( )
1035 =cut
1036 sub spawn_ip {
1037 my ($lbi) = @_;
1039 # Cosmetics.
1040 $lbi->debug( "spawning new IP\n" );
1042 # Cloning and storing new IP.
1043 my $newip = $lbi->get_curip->clone;
1044 $newip->dir_reverse;
1045 $lbi->move_ip($newip);
1046 push @{ $lbi->get_newips }, $newip;
1049 =back
1053 =head2 Library semantics
1055 =over 4
1057 =item lib_load( )
1059 =cut
1060 sub lib_load {
1061 my ($lbi) = @_;
1062 my $ip = $lbi->get_curip;
1064 # Fetching fingerprint.
1065 my $count = $ip->spop;
1066 my $fgrprt = 0;
1067 while ( $count-- > 0 ) {
1068 my $val = $ip->spop;
1069 $lbi->abort( "Attempt to build a fingerprint with a negative number" )
1070 if $val < 0;
1071 $fgrprt = $fgrprt * 256 + $val;
1074 # Transform the fingerprint into a library name.
1075 my $lib = "";
1076 my $finger = $fgrprt;
1077 while ( $finger > 0 ) {
1078 my $c = $finger % 0x100;
1079 $lib .= chr($c);
1080 $finger = int ( $finger / 0x100 );
1082 $lib = "Language::Befunge::lib::" . reverse $lib;
1084 # Checking if library exists.
1085 eval "require $lib";
1086 if ( $@ ) {
1087 $lbi->debug( sprintf("unknown extension $lib (0x%x): reversing\n", $fgrprt) );
1088 $ip->dir_reverse;
1089 } else {
1090 $lbi->debug( sprintf("extension $lib (0x%x) loaded\n", $fgrprt) );
1091 my $obj = new $lib;
1092 $ip->load( $obj );
1093 $ip->spush( $fgrprt, 1 );
1098 =item lib_unload( )
1100 =cut
1101 sub lib_unload {
1102 my ($lbi) = @_;
1103 my $ip = $lbi->get_curip;
1105 # Fetching fingerprint.
1106 my $count = $ip->spop;
1107 my $fgrprt = 0;
1108 while ( $count-- > 0 ) {
1109 my $val = $ip->spop;
1110 $lbi->abort( "Attempt to build a fingerprint with a negative number" )
1111 if $val < 0;
1112 $fgrprt = $fgrprt * 256 + $val;
1115 # Transform the fingerprint into a library name.
1116 my $lib = "";
1117 my $finger = $fgrprt;
1118 while ( $finger > 0 ) {
1119 my $c = $finger % 0x100;
1120 $lib .= chr($c);
1121 $finger = int ( $finger / 0x100 );
1123 $lib = "Language::Befunge::lib::" . reverse $lib;
1125 # Unload the library.
1126 if ( defined( $ip->unload($lib) ) ) {
1127 $lbi->debug( sprintf("unloading library $lib (0x%x)\n", $fgrprt) );
1128 } else {
1129 # The library wasn't loaded.
1130 $lbi->debug( sprintf("library $lib (0x%x) wasn't loaded\n", $fgrprt) );
1131 $ip->dir_reverse;
1135 =item lib_run_instruction( )
1137 =cut
1139 sub lib_run_instruction {
1140 my ($lbi) = @_;
1141 my $ip = $lbi->get_curip;
1142 my $char = $lbi->storage->get_char( $ip->get_position );
1144 # Maybe a library semantics.
1145 $lbi->debug( "library semantics\n" );
1147 foreach my $obj ( @{ $ip->get_libs } ) {
1148 # Try the loaded libraries in order.
1149 eval "\$obj->$char(\$lbi)";
1150 if( $@ ) {
1151 $lbi->debug( ref($obj) . "->$char failed: $@" );
1152 next;
1155 # We manage to get a library.
1156 $lbi->debug( "library semantics processed by ".ref($obj)."\n" );
1157 return;
1160 # Non-overloaded capitals default to reverse.
1161 $lbi->debug("no library semantics found: reversing\n");
1162 $ip->dir_reverse;
1165 =back
1167 =cut
1171 __END__
1173 =head1 SEE ALSO
1175 L<Language::Befunge>
1178 =head1 AUTHOR
1180 Jerome Quelin, E<lt>jquelin@cpan.orgE<gt>
1182 Development is discussed on E<lt>language-befunge@mongueurs.netE<gt>
1185 =head1 COPYRIGHT & LICENSE
1187 Copyright (c) 2001-2008 Jerome Quelin, all rights reserved.
1189 This program is free software; you can redistribute it and/or modify
1190 it under the same terms as Perl itself.
1193 =cut