File Coverage

blib/lib/Language/Befunge/Ops.pm
Criterion Covered Total %
statement 433 435 99.5
branch 93 94 98.9
condition 6 6 100.0
subroutine 59 59 100.0
pod 55 55 100.0
total 646 649 99.5


line stmt bran cond sub pod time code
1             #
2             # This file is part of Language::Befunge.
3             # Copyright (c) 2001-2009 Jerome Quelin, all rights reserved.
4             #
5             # This program is free software; you can redistribute it and/or modify
6             # it under the same terms as Perl itself.
7             #
8             #
9              
10             package Language::Befunge::Ops;
11             require 5.010;
12              
13 68     68   117576 use strict;
  68         141  
  68         3158  
14 68     68   382 use warnings;
  68         194  
  68         2903  
15              
16 68     68   130644 use File::Spec::Functions qw{ catfile }; # For the 'y' instruction.
  68         112095  
  68         8828  
17 68     68   43873 use Language::Befunge::Debug;
  68         166  
  68         1874709  
18              
19              
20             =head1 NAME
21              
22             Language::Befunge::Ops - definition of the various operations
23              
24              
25             =head1 DESCRIPTION
26              
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.
30              
31              
32             =head1 SUBROUTINES
33              
34             =head2 Numbers
35              
36             =over 4
37              
38             =item num_push_number( )
39              
40             Push the current number onto the TOSS.
41              
42             =cut
43             sub num_push_number {
44 650     650 1 1016 my ($lbi, $char) = @_;
45              
46             # Fetching char.
47 650         1204 my $ip = $lbi->get_curip;
48 650         1146 my $num = hex( $char );
49              
50             # Pushing value.
51 650         9747 $ip->spush( $num );
52              
53             # Cosmetics.
54 650         2468 debug( "pushing number '$num'\n" );
55             }
56              
57             =back
58              
59              
60              
61             =head2 Strings
62              
63             =over 4
64              
65             =item str_enter_string_mode( )
66              
67             =cut
68             sub str_enter_string_mode {
69 37     37 1 85 my ($lbi) = @_;
70              
71             # Cosmetics.
72 37         112 debug( "entering string mode\n" );
73              
74             # Entering string-mode.
75 37         203 $lbi->get_curip->set_string_mode(1);
76             }
77              
78              
79             =item str_fetch_char( )
80              
81             =cut
82             sub str_fetch_char {
83 29     29 1 36 my ($lbi) = @_;
84 29         42 my $ip = $lbi->get_curip;
85              
86             # Moving pointer...
87 29         75 $lbi->_move_ip_once($lbi->get_curip);
88              
89             # .. then fetch value and push it.
90 29         120 my $ord = $lbi->get_storage->get_value( $ip->get_position );
91 29         99 my $chr = $lbi->get_storage->get_char( $ip->get_position );
92 29         74 $ip->spush( $ord );
93              
94             # Cosmetics.
95 29         91 debug( "pushing value $ord (char='$chr')\n" );
96             }
97              
98              
99             =item str_store_char( )
100              
101             =cut
102             sub str_store_char {
103 2     2 1 8 my ($lbi) = @_;
104 2         7 my $ip = $lbi->get_curip;
105              
106             # Moving pointer.
107 2         11 $lbi->_move_ip_once($lbi->get_curip);
108              
109             # Fetching value.
110 2         24 my $val = $ip->spop;
111              
112             # Storing value.
113 2         17 $lbi->get_storage->set_value( $ip->get_position, $val );
114 2         15 my $chr = $lbi->get_storage->get_char( $ip->get_position );
115              
116             # Cosmetics.
117 2         27 debug( "storing value $val (char='$chr')\n" );
118             }
119              
120             =back
121              
122              
123              
124             =head2 Mathematical operations
125              
126             =over 4
127              
128             =item math_addition( )
129              
130             =cut
131             sub math_addition {
132 86     86 1 299 my ($lbi) = @_;
133 86         178 my $ip = $lbi->get_curip;
134              
135             # Fetching values.
136 86         313 my ($v1, $v2) = $ip->spop_mult(2);
137 86         394 debug( "adding: $v1+$v2\n" );
138 86         181 my $res = $v1 + $v2;
139              
140             # Checking over/underflow.
141 86 100       257 $res > 2**31-1 and $lbi->abort( "program overflow while performing addition" );
142 84 100       214 $res < -2**31 and $lbi->abort( "program underflow while performing addition" );
143              
144             # Pushing value.
145 82         249 $ip->spush( $res );
146             }
147              
148              
149             =item math_substraction( )
150              
151             =cut
152             sub math_substraction {
153 42     42 1 138 my ($lbi) = @_;
154 42         110 my $ip = $lbi->get_curip;
155              
156             # Fetching values.
157 42         169 my ($v1, $v2) = $ip->spop_mult(2);
158 42         1511 debug( "substracting: $v1-$v2\n" );
159 42         161 my $res = $v1 - $v2;
160              
161             # checking over/underflow.
162 42 100       139 $res > 2**31-1 and $lbi->abort( "program overflow while performing substraction" );
163 40 100       131 $res < -2**31 and $lbi->abort( "program underflow while performing substraction" );
164              
165             # Pushing value.
166 38         127 $ip->spush( $res );
167             }
168              
169              
170             =item math_multiplication( )
171              
172             =cut
173             sub math_multiplication {
174 73     73 1 196 my ($lbi) = @_;
175 73         135 my $ip = $lbi->get_curip;
176              
177             # Fetching values.
178 73         215 my ($v1, $v2) = $ip->spop_mult(2);
179 73         297 debug( "multiplicating: $v1*$v2\n" );
180 73         119 my $res = $v1 * $v2;
181              
182             # checking over/underflow.
183 73 100       195 $res > 2**31-1 and $lbi->abort( "program overflow while performing multiplication" );
184 71 100       150 $res < -2**31 and $lbi->abort( "program underflow while performing multiplication" );
185              
186             # Pushing value.
187 69         271 $ip->spush( $res );
188             }
189              
190              
191             =item math_division( )
192              
193             =cut
194             sub math_division {
195 7     7 1 15 my ($lbi) = @_;
196 7         17 my $ip = $lbi->get_curip;
197              
198             # Fetching values.
199 7         22 my ($v1, $v2) = $ip->spop_mult(2);
200 7         34 debug( "dividing: $v1/$v2\n" );
201 7 100       27 my $res = $v2 == 0 ? 0 : int($v1 / $v2);
202              
203             # Can't do over/underflow with integer division.
204              
205             # Pushing value.
206 7         22 $ip->spush( $res );
207             }
208              
209              
210             =item math_remainder( )
211              
212             =cut
213             sub math_remainder {
214 5     5 1 13 my ($lbi) = @_;
215 5         17 my $ip = $lbi->get_curip;
216              
217             # Fetching values.
218 5         20 my ($v1, $v2) = $ip->spop_mult(2);
219 5         28 debug( "remainder: $v1%$v2\n" );
220 5 100       20 my $res = $v2 == 0 ? 0 : int($v1 % $v2);
221              
222             # Can't do over/underflow with integer remainder.
223              
224             # Pushing value.
225 5         18 $ip->spush( $res );
226             }
227              
228             =back
229              
230              
231              
232             =head2 Direction changing
233              
234             =over 4
235              
236             =item dir_go_east( )
237              
238             =cut
239             sub dir_go_east {
240 106     106 1 202 my ($lbi) = @_;
241 106         312 debug( "going east\n" );
242 106         458 $lbi->get_curip->dir_go_east;
243             }
244              
245              
246             =item dir_go_west( )
247              
248             =cut
249             sub dir_go_west {
250 50     50 1 115 my ($lbi) = @_;
251 50         156 debug( "going west\n" );
252 50         262 $lbi->get_curip->dir_go_west;
253             }
254              
255              
256             =item dir_go_north( )
257              
258             =cut
259             sub dir_go_north {
260 23     23 1 69 my ($lbi) = @_;
261 23         76 debug( "going north\n" );
262 23         132 $lbi->get_curip->dir_go_north;
263             }
264              
265              
266             =item dir_go_south( )
267              
268             =cut
269             sub dir_go_south {
270 68     68 1 142 my ($lbi) = @_;
271 68         240 debug( "going south\n" );
272 68         331 $lbi->get_curip->dir_go_south;
273             }
274              
275              
276             =item dir_go_high( )
277              
278             =cut
279             sub dir_go_high {
280 2     2 1 25 my ($lbi) = @_;
281 2         10 debug( "going high\n" );
282 2         14 $lbi->get_curip->dir_go_high;
283             }
284              
285              
286             =item dir_go_low( )
287              
288             =cut
289             sub dir_go_low {
290 1     1 1 25 my ($lbi) = @_;
291 1         8 debug( "going low\n" );
292 1         8 $lbi->get_curip->dir_go_low;
293             }
294              
295              
296             =item dir_go_away( )
297              
298             =cut
299             sub dir_go_away {
300 2     2 1 23 my ($lbi) = @_;
301 2         11 debug( "going away!\n" );
302 2         15 $lbi->get_curip->dir_go_away;
303             }
304              
305              
306             =item dir_turn_left( )
307              
308             Turning left, like a car (the specs speak about a bicycle, but perl
309             is _so_ fast that we can speak about cars ;) ).
310              
311             =cut
312             sub dir_turn_left {
313 5     5 1 35 my ($lbi) = @_;
314 5         15 debug( "turning on the left\n" );
315 5         52 $lbi->get_curip->dir_turn_left;
316             }
317              
318              
319             =item dir_turn_right( )
320              
321             Turning right, like a car (the specs speak about a bicycle, but perl
322             is _so_ fast that we can speak about cars ;) ).
323              
324             =cut
325             sub dir_turn_right {
326 5     5 1 34 my ($lbi) = @_;
327 5         20 debug( "turning on the right\n" );
328 5         36 $lbi->get_curip->dir_turn_right;
329             }
330              
331              
332             =item dir_reverse( )
333              
334             =cut
335             sub dir_reverse {
336 5     5 1 29 my ($lbi) = @_;
337 5         17 debug( "180 deg!\n" );
338 5         25 $lbi->get_curip->dir_reverse;
339             }
340              
341              
342             =item dir_set_delta( )
343              
344             Hmm, the user seems to know where he wants to go. Let's trust him/her.
345              
346             =cut
347             sub dir_set_delta {
348 6     6 1 31 my ($lbi) = @_;
349 6         23 my $ip = $lbi->get_curip;
350 6         32 my ($new_d) = $ip->spop_vec;
351 6         80 debug( "setting delta to $new_d\n" );
352 6         32 $ip->set_delta( $new_d );
353             }
354              
355             =back
356              
357              
358              
359             =head2 Decision making
360              
361             =over 4
362              
363             =item decis_neg( )
364              
365             =cut
366             sub decis_neg {
367 21     21 1 44 my ($lbi) = @_;
368 21         55 my $ip = $lbi->get_curip;
369              
370             # Fetching value.
371 21 100       63 my $val = $ip->spop ? 0 : 1;
372 21         64 $ip->spush( $val );
373              
374 21         85 debug( "logical not: pushing $val\n" );
375             }
376              
377              
378             =item decis_gt( )
379              
380             =cut
381             sub decis_gt {
382 6     6 1 101 my ($lbi) = @_;
383 6         17 my $ip = $lbi->get_curip;
384              
385             # Fetching values.
386 6         22 my ($v1, $v2) = $ip->spop_mult(2);
387 6         31 debug( "comparing $v1 vs $v2\n" );
388 6 100       27 $ip->spush( ($v1 > $v2) ? 1 : 0 );
389             }
390              
391              
392             =item decis_horiz_if( )
393              
394             =cut
395             sub decis_horiz_if {
396 70     70 1 115 my ($lbi) = @_;
397 70         137 my $ip = $lbi->get_curip;
398              
399             # Fetching value.
400 70         334 my $val = $ip->spop;
401 70 100       285 $val ? $ip->dir_go_west : $ip->dir_go_east;
402 70 100       321 debug( "horizontal if: going " . ( $val ? "west\n" : "east\n" ) );
403             }
404              
405              
406             =item decis_vert_if( )
407              
408             =cut
409             sub decis_vert_if {
410 9     9 1 31 my ($lbi) = @_;
411 9         31 my $ip = $lbi->get_curip;
412              
413             # Fetching value.
414 9         36 my $val = $ip->spop;
415 9 100       47 $val ? $ip->dir_go_north : $ip->dir_go_south;
416 9 100       64 debug( "vertical if: going " . ( $val ? "north\n" : "south\n" ) );
417             }
418              
419              
420             =item decis_z_if( )
421              
422             =cut
423             sub decis_z_if {
424 2     2 1 8 my ($lbi) = @_;
425 2         7 my $ip = $lbi->get_curip;
426              
427             # Fetching value.
428 2         9 my $val = $ip->spop;
429 2 100       23 $val ? $ip->dir_go_low : $ip->dir_go_high;
430 2 100       15 debug( "z if: going " . ( $val ? "low\n" : "high\n" ) );
431             }
432              
433              
434             =item decis_cmp( )
435              
436             =cut
437             sub decis_cmp {
438 6     6 1 13 my ($lbi) = @_;
439 6         32 my $ip = $lbi->get_curip;
440              
441             # Fetching value.
442 6         24 my ($v1, $v2) = $ip->spop_mult(2);
443 6 100       24 debug( "comparing $v1 with $v2: straight forward!\n"), return if $v1 == $v2;
444              
445 4         6 my $dir;
446 4 100       9 if ( $v1 < $v2 ) {
447 2         9 $ip->dir_turn_left;
448 2         4 $dir = "left";
449             } else {
450 2         10 $ip->dir_turn_right;
451 2         2 $dir = "right";
452             }
453 4         23 debug( "comparing $v1 with $v2: turning: $dir\n" );
454             }
455              
456             =back
457              
458              
459              
460             =head2 Flow control
461              
462             =over 4
463              
464             =item flow_space( )
465              
466             A serie of spaces is to be treated as B NO-OP.
467              
468             =cut
469             sub flow_space {
470 3     3 1 12 my ($lbi) = @_;
471 3         18 my $ip = $lbi->get_curip;
472 3         27 $lbi->_move_ip_till($ip, qr/ /);
473 3         27 $lbi->move_ip($lbi->get_curip);
474              
475 3         20 my $char = $lbi->get_storage->get_char($ip->get_position);
476 3         29 $lbi->_do_instruction($char);
477             }
478              
479              
480             =item flow_no_op( )
481              
482             =cut
483             sub flow_no_op {
484 41     41 1 68 my ($lbi) = @_;
485 41         98 debug( "no-op\n" );
486             }
487              
488              
489             =item flow_comments( )
490              
491             Bypass comments in B tick.
492              
493             =cut
494             sub flow_comments {
495 2     2 1 18 my ($lbi) = @_;
496 2         7 my $ip = $lbi->get_curip;
497              
498 2         7 $lbi->_move_ip_once($ip); # skip comment ';'
499 2         15 $lbi->_move_ip_till( $ip, qr/[^;]/ ); # till just before matching ';'
500 2         11 $lbi->_move_ip_once($ip); # till matching ';'
501 2         9 $lbi->_move_ip_once($ip); # till just after matching ';'
502              
503 2         10 my $char = $lbi->get_storage->get_char($ip->get_position);
504 2         8 $lbi->_do_instruction($char);
505             }
506              
507              
508             =item flow_trampoline( )
509              
510             =cut
511             sub flow_trampoline {
512 256     256 1 459 my ($lbi) = @_;
513 256         963 $lbi->_move_ip_once($lbi->get_curip);
514 256         981 debug( "trampoline! (skipping next instruction)\n" );
515             }
516              
517              
518             =item flow_jump_to( )
519              
520             =cut
521             sub flow_jump_to {
522 6     6 1 16 my ($lbi) = @_;
523 6         17 my $ip = $lbi->get_curip;
524 6         19 my $count = $ip->spop;
525 6         27 debug( "skipping $count instructions\n" );
526 6 100       18 $count == 0 and return;
527 4 100       16 $count < 0 and $ip->dir_reverse; # We can move backward.
528 4         24 $lbi->_move_ip_once($lbi->get_curip) for (1..abs($count));
529 4 100       21 $count < 0 and $ip->dir_reverse;
530             }
531              
532              
533             =item flow_repeat( )
534              
535             =cut
536             sub flow_repeat {
537 13     13 1 31 my ($lbi) = @_;
538 13         45 my $ip = $lbi->get_curip;
539 13         36 my $pos = $ip->get_position;
540              
541 13         49 my $kcounter = $ip->spop;
542 13         59 debug( "repeating next instruction $kcounter times.\n" );
543              
544             # fetch instruction to repeat
545 13         61 $lbi->move_ip($lbi->get_curip);
546 13         97 my $char = $lbi->get_storage->get_char($ip->get_position);
547              
548 13 100       56 $char eq 'k' and return; # k cannot be itself repeated
549 12 100       38 $kcounter == 0 and return; # nothing to repeat
550 10 100       29 $kcounter < 0 and return; # oops, error
551              
552             # reset position back to where k is, and repeat instruction
553 9         34 $ip->set_position($pos);
554 9         59 $lbi->_do_instruction($char) for (1..$kcounter);
555             }
556              
557              
558             =item flow_kill_thread( )
559              
560             =cut
561             sub flow_kill_thread {
562 19     19 1 53 my ($lbi) = @_;
563 19         63 debug( "end of Instruction Pointer\n" );
564 19         172 $lbi->get_curip->set_end('@');
565             }
566              
567              
568             =item flow_quit( )
569              
570             =cut
571             sub flow_quit {
572 186     186 1 358 my ($lbi) = @_;
573 186         543 debug( "end program\n" );
574 186         571 $lbi->set_newips( [] );
575 186         402 $lbi->set_ips( [] );
576 186         743 $lbi->get_curip->set_end('q');
577 186         772 $lbi->set_retval( $lbi->get_curip->spop );
578             }
579              
580             =back
581              
582              
583              
584             =head2 Stack manipulation
585              
586             =over 4
587              
588             =item stack_pop( )
589              
590             =cut
591             sub stack_pop {
592 63     63 1 79 my ($lbi) = @_;
593 63         150 debug( "popping a value\n" );
594 63         187 $lbi->get_curip->spop;
595             }
596              
597              
598             =item stack_duplicate( )
599              
600             =cut
601             sub stack_duplicate {
602 70     70 1 112 my ($lbi) = @_;
603 70         138 my $ip = $lbi->get_curip;
604 70         207 my $value = $ip->spop;
605 70         289 debug( "duplicating value '$value'\n" );
606 70         232 $ip->spush( $value );
607 70         205 $ip->spush( $value );
608             }
609              
610              
611             =item stack_swap( )
612              
613             =cut
614             sub stack_swap {
615 3     3 1 12 my ($lbi) = @_;
616 3         9 my $ ip = $lbi->get_curip;
617 3         18 my ($v1, $v2) = $ip->spop_mult(2);
618 3         21 debug( "swapping $v1 and $v2\n" );
619 3         84 $ip->spush( $v2 );
620 3         11 $ip->spush( $v1 );
621             }
622              
623              
624             =item stack_clear( )
625              
626             =cut
627             sub stack_clear {
628 2     2 1 8 my ($lbi) = @_;
629 2         11 debug( "clearing stack\n" );
630 2         14 $lbi->get_curip->sclear;
631             }
632              
633             =back
634              
635              
636              
637             =head2 Stack stack manipulation
638              
639             =over 4
640              
641             =item block_open( )
642              
643             =cut
644             sub block_open {
645 19     19 1 35 my ($lbi) = @_;
646 19         54 my $ip = $lbi->get_curip;
647 19         63 debug( "block opening\n" );
648              
649             # Create new TOSS.
650 19         81 $ip->ss_create( $ip->spop );
651              
652             # Store current storage offset on SOSS.
653 19         130 $ip->soss_push( $ip->get_storage->get_all_components );
654              
655             # Set the new Storage Offset.
656 19         87 $lbi->_move_ip_once($lbi->get_curip);
657 19         92 $ip->set_storage( $ip->get_position );
658 19         63 $ip->dir_reverse;
659 19         84 $lbi->_move_ip_once($lbi->get_curip);
660 19         88 $ip->dir_reverse;
661             }
662              
663              
664             =item block_close( )
665              
666             =cut
667             sub block_close {
668 12     12 1 27 my ($lbi) = @_;
669 12         36 my $ip = $lbi->get_curip;
670              
671             # No opened block.
672 12 100       61 $ip->ss_count <= 0 and $ip->dir_reverse, debug("no opened block\n"), return;
673              
674 10         48 debug( "block closing\n" );
675              
676             # Restore Storage offset.
677 10         42 $ip->set_storage( $ip->soss_pop_vec );
678              
679             # Remove the TOSS.
680 10         39 $ip->ss_remove( $ip->spop );
681             }
682              
683              
684             =item bloc_transfer( )
685              
686             =cut
687             sub bloc_transfer {
688 11     11 1 17 my ($lbi) = @_;
689 11         19 my $ip = $lbi->get_curip;
690              
691 11 100       34 $ip->ss_count <= 0 and $ip->dir_reverse, debug("no SOSS available\n"), return;
692              
693             # Transfering values.
694 10         28 debug( "transfering values\n" );
695 10         42 $ip->ss_transfer( $ip->spop );
696             }
697              
698             =back
699              
700              
701              
702             =head2 Funge-space storage
703              
704             =over 4
705              
706             =item store_get( )
707              
708             =cut
709             sub store_get {
710 4     4 1 9 my ($lbi) = @_;
711 4         12 my $ip = $lbi->get_curip;
712              
713             # Fetching coordinates.
714 4         21 my ($v) = $ip->spop_vec;
715 4         20 $v += $ip->get_storage;
716              
717             # Fetching char.
718 4         18 my $val = $lbi->get_storage->get_value( $v );
719 4         15 $ip->spush( $val );
720              
721 4         16 debug( "fetching value at $v: pushing $val\n" );
722             }
723              
724              
725             =item store_put( )
726              
727             =cut
728             sub store_put {
729 15     15 1 105 my ($lbi) = @_;
730 15         52 my $ip = $lbi->get_curip;
731              
732             # Fetching coordinates.
733 15         70 my ($v) = $ip->spop_vec;
734 15         83 $v += $ip->get_storage;
735              
736             # Fetching char.
737 15         70 my $val = $ip->spop;
738 15         93 $lbi->get_storage->set_value( $v, $val );
739              
740 15         72 debug( "storing value $val at $v\n" );
741             }
742              
743             =back
744              
745              
746              
747             =head2 Standard Input/Output
748              
749             =over 4
750              
751             =item stdio_out_num( )
752              
753             =cut
754             sub stdio_out_num {
755 212     212 1 462 my ($lbi) = @_;
756 212         461 my $ip = $lbi->get_curip;
757              
758             # Fetch value and print it.
759 212         714 my $val = $ip->spop;
760 212         1001 debug( "numeric output: $val\n");
761 212 100       12610 print( "$val " ) or $ip->dir_reverse;
762             }
763              
764              
765             =item stdio_out_ascii( )
766              
767             =cut
768             sub stdio_out_ascii {
769 150     150 1 213 my ($lbi) = @_;
770 150         260 my $ip = $lbi->get_curip;
771              
772             # Fetch value and print it.
773 150         433 my $val = $ip->spop;
774 150         248 my $chr = chr $val;
775 150         531 debug( "ascii output: '$chr' (ord=$val)\n");
776 150 100       4850 print( $chr ) or $ip->dir_reverse;
777             }
778              
779              
780             =item stdio_in_num( )
781              
782             =cut
783             sub stdio_in_num {
784 12     12 1 18866 my ($lbi) = @_;
785 12         174 my $ip = $lbi->get_curip;
786 12         105 my ($in, $nb) = ('', 0);
787 12         69 my $last = 0;
788 12         100 while(!$last) {
789 59         404 my $char = $lbi->get_input();
790 59 100       220 $in .= $char if defined $char;
791 59         136 my $overflow;
792 59         595 ($nb, $overflow) = $in =~ /(-?\d+)(\D*)$/;
793 59 100 100     1871 if((defined($overflow) && length($overflow)) || !defined($char)) {
      100        
794             # either we found a non-digit character: $overflow
795             # or else we reached EOF: !$char
796 12 100       73 return $ip->dir_reverse() unless defined $nb;
797 6 100       85 $nb < -2**31 and $nb = -2**31;
798 6 100       43 $nb > 2**31-1 and $nb = 2**31-1;
799 6         14 $in = $overflow;
800 6         21 $last++;
801             }
802             }
803 6         32 $lbi->set_input( $in );
804 6         160 $ip->spush( $nb );
805 6         189 debug( "numeric input: pushing $nb\n" );
806             }
807              
808              
809             =item stdio_in_ascii( )
810              
811             =cut
812             sub stdio_in_ascii {
813 3     3 1 1737 my ($lbi) = @_;
814 3         47 my $ip = $lbi->get_curip;
815 3         62 my $in = $lbi->get_input();
816 3 100       32 return $ip->dir_reverse unless defined $in;
817 2         16 my $ord = ord $in;
818 2         32 $ip->spush( $ord );
819 2         43 debug( "ascii input: pushing $ord\n" );
820             }
821              
822              
823             =item stdio_in_file( )
824              
825             =cut
826             sub stdio_in_file {
827 3     3 1 6 my ($lbi) = @_;
828 3         10 my $ip = $lbi->get_curip;
829              
830             # Fetch arguments.
831 3         15 my $path = $ip->spop_gnirts;
832 3         23 my $flag = $ip->spop;
833 3         15 my ($vin) = $ip->spop_vec;
834 3         16 $vin += $ip->get_storage;
835              
836             # Read file.
837 3         17 debug( "input file '$path' at $vin\n" );
838 3 100       295 open F, "<", $path or $ip->dir_reverse, return;
839 2         5 my $lines;
840             {
841 2         4 local $/; # slurp mode.
  2         16  
842 2         83 $lines = ;
843             }
844 2         37 close F;
845              
846             # Store the code and the result vector.
847 2 100       24 my ($size) = $flag % 2
848             ? ( $lbi->get_storage->store_binary( $lines, $vin ) )
849             : ( $lbi->get_storage->store( $lines, $vin ) );
850 2         13 $ip->spush_vec( $size, $vin );
851             }
852              
853              
854             =item stdio_out_file( )
855              
856             =cut
857             sub stdio_out_file {
858 3     3 1 10 my ($lbi) = @_;
859 3         9 my $ip = $lbi->get_curip;
860              
861             # Fetch arguments.
862 3         16 my $path = $ip->spop_gnirts;
863 3         20 my $flag = $ip->spop;
864 3         18 my ($vin) = $ip->spop_vec;
865 3         21 $vin += $ip->get_storage;
866 3         13 my ($size) = $ip->spop_vec;
867 3         25 my $data = $lbi->get_storage->rectangle( $vin, $size );
868              
869             # Cosmetics.
870 3         16 my $vend = $vin + $size;
871 3         14 debug( "output $vin-$vend to '$path'\n" );
872              
873             # Treat the data chunk as text file?
874 3 100       12 if ( $flag & 0x1 ) {
875 1         14 $data =~ s/ +$//mg; # blank lines are now void.
876 1         6 $data =~ s/\n+\z/\n/; # final blank lines are stripped.
877             }
878              
879             # Write file.
880 3 100       1216 open F, ">", $path or $ip->dir_reverse, return;
881 2         20 print F $data;
882 2         229 close F;
883             }
884              
885              
886             =item stdio_sys_exec( )
887              
888             =cut
889             sub stdio_sys_exec {
890 2     2 1 4 my ($lbi) = @_;
891 2         7 my $ip = $lbi->get_curip;
892              
893             # Fetching command.
894 2         15 my $path = $ip->spop_gnirts;
895 2         11 debug( "spawning external command: $path\n" );
896 2         73960 system( $path );
897 2 100       194 $ip->spush( $? == -1 ? -1 : $? >> 8 );
898             }
899              
900             =back
901              
902              
903              
904             =head2 System info retrieval
905              
906             =over 4
907              
908             =item sys_info( )
909              
910             =cut
911             sub sys_info {
912 30     30 1 65 my ($lbi) = @_;
913 30         123 my $ip = $lbi->get_curip;
914 30         63 my $storage = $lbi->get_storage;
915              
916 30         103 my $val = $ip->spop;
917 30         62 my @infos = ();
918              
919             # 1. flags
920 30         61 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).
925              
926             # 2. number of bytes per cell.
927             # 32 bytes Funge: 4 bytes.
928 30         45 push @infos, 4;
929              
930             # 3. implementation handprint.
931 30         64 my $handprint = 0;
932 30         328 $handprint = $handprint * 256 + ord($_) for split //, $lbi->get_handprint;
933 30         74 push @infos, $handprint;
934              
935             # 4. version number.
936 30         73 my $ver = $Language::Befunge::VERSION;
937 30         248 $ver =~ s/\D//g;
938 30         59 push @infos, $ver;
939              
940             # 5. ID code for Operating Paradigm.
941 30         51 push @infos, 1; # C-language system() call behaviour.
942              
943             # 6. Path separator character.
944 30         192 push @infos, ord( catfile('','') );
945              
946             # 7. Number of dimensions.
947 30         118 push @infos, $ip->get_dims;
948              
949             # 8. Unique IP number.
950 30         69 push @infos, $ip->get_id;
951              
952             # 9. Unique team number for the IP (NetFunge, not implemented).
953 30         61 push @infos, 0;
954              
955             # 10. Position of the curent IP.
956 30         139 my @pos = ( $ip->get_position->get_all_components );
957 30         75 push @infos, \@pos;
958              
959             # 11. Delta of the curent IP.
960 30         121 my @delta = ( $ip->get_delta->get_all_components );
961 30         80 push @infos, \@delta;
962              
963             # 12. Storage offset of the curent IP.
964 30         148 my @stor = ( $ip->get_storage->get_all_components );
965 30         56 push @infos, \@stor;
966              
967             # 13. Top-left point.
968 30         105 my $min = $storage->min;
969             # FIXME: multiple dims?
970 30         180 my @topleft = ( $min->get_component(0), $min->get_component(1) );
971 30         49 push @infos, \@topleft;
972              
973             # 14. Dims of the storage.
974 30         127 my $max = $storage->max;
975             # FIXME: multiple dims?
976 30         216 my @dims = ( $max->get_component(0) - $min->get_component(0),
977             $max->get_component(1) - $min->get_component(1) );
978 30         61 push @infos, \@dims;
979              
980             # 15/16. Current date/time.
981 30         1539 my ($s,$m,$h,$dd,$mm,$yy)=localtime;
982 30         117 push @infos, $yy*256*256 + ($mm+1)*256 + $dd;
983 30         85 push @infos, $h*256*256 + $m*256 + $s;
984              
985             # 17. Size of stack stack.
986 30         143 push @infos, $ip->ss_count + 1;
987              
988             # 18. Size of each stack in the stack stack.
989             # note: the number of stack is given by previous value.
990 30         174 my @sizes = reverse $ip->ss_sizes;
991 30         59 push @infos, \@sizes;
992              
993             # 19. $file + params.
994 30         125 my $str = join chr(0), $lbi->get_file, @{$lbi->get_params}, chr(0)x2;
  30         143  
995 30         147 my @cmdline = reverse map { ord } split //, $str;
  250         392  
996 30         96 push @infos, \@cmdline;
997              
998             # 20. %ENV
999             # 00EULAV=EMAN0EULAV=EMAN
1000 30         49 $str = "";
1001 30         972 $str .= "$_=$ENV{$_}".chr(0) foreach sort keys %ENV;
1002 30         81 $str .= chr(0);
1003 30         7616 my @env = reverse map { ord } split //, $str;
  16235         22635  
1004 30         1656 push @infos, \@env;
1005              
1006 30 100       72 my @cells = map { ref($_) eq 'ARRAY' ? (@$_) : ($_) } reverse @infos;
  600         7982  
1007              
1008             # Okay, what to do with those cells.
1009 30 100       517 if ( $val <= 0 ) {
    100          
1010             # Blindly push them onto the stack.
1011 3         14 debug( "system info: pushing the whole stuff\n" );
1012 3         29 $ip->spush(@cells);
1013              
1014             } elsif ( $val <= scalar(@cells) ) {
1015             # Only push the wanted value.
1016 26         184 debug( "system info: pushing the ${val}th value\n" );
1017 26         168 $ip->spush( $cells[$#cells-$val+1] );
1018              
1019             } else {
1020             # Pick a given value in the stack and push it.
1021 1         4 my $offset = $val - $#cells - 1;
1022 1         6 my $value = $ip->svalue($offset);
1023 1         8 debug( "system info: picking the ${offset}th value from the stack = $value\n" );
1024 1         5 $ip->spush( $value );
1025             }
1026             }
1027              
1028             =back
1029              
1030              
1031              
1032             =head2 Concurrent Funge
1033              
1034             =over 4
1035              
1036             =item spawn_ip( )
1037              
1038             =cut
1039             sub spawn_ip {
1040 6     6 1 13 my ($lbi) = @_;
1041              
1042             # Cosmetics.
1043 6         17 debug( "spawning new IP\n" );
1044              
1045             # Cloning and storing new IP.
1046 6         29 my $newip = $lbi->get_curip->clone;
1047 6         26 $newip->dir_reverse;
1048 6         22 $lbi->move_ip($newip);
1049 6         12 push @{ $lbi->get_newips }, $newip;
  6         28  
1050             }
1051              
1052             =back
1053              
1054              
1055              
1056             =head2 Library semantics
1057              
1058             =over 4
1059              
1060             =item lib_load( )
1061              
1062             =cut
1063             sub lib_load {
1064 19     19 1 30 my ($lbi) = @_;
1065 19         41 my $ip = $lbi->get_curip;
1066              
1067             # Fetching fingerprint.
1068 19         60 my $count = $ip->spop;
1069 19         27 my $fgrprt = 0;
1070 19         58 while ( $count-- > 0 ) {
1071 70         150 my $val = $ip->spop;
1072 70 100       142 $lbi->abort( "Attempt to build a fingerprint with a negative number" )
1073             if $val < 0;
1074 69         148 $fgrprt = $fgrprt * 256 + $val;
1075             }
1076              
1077             # Transform the fingerprint into a library name.
1078 18         27 my $lib = "";
1079 18         22 my $finger = $fgrprt;
1080 18         66 while ( $finger > 0 ) {
1081 69         83 my $c = $finger % 0x100;
1082 69         80 $lib .= chr($c);
1083 69         341 $finger = int ( $finger / 0x100 );
1084             }
1085 18         44 $lib = "Language::Befunge::lib::" . reverse $lib;
1086              
1087             # Checking if library exists.
1088 18         1412 eval "require $lib";
1089 18 100       87 if ( $@ ) {
1090 1         12 debug( sprintf("unknown extension $lib (0x%x): reversing\n", $fgrprt) );
1091 1         5 $ip->dir_reverse;
1092             } else {
1093 17         134 debug( sprintf("extension $lib (0x%x) loaded\n", $fgrprt) );
1094 17         108 my $obj = $lib->new;
1095 17         74 $ip->load( $obj );
1096 17         65 $ip->spush( $fgrprt, 1 );
1097             }
1098             }
1099              
1100              
1101             =item lib_unload( )
1102              
1103             =cut
1104             sub lib_unload {
1105 10     10 1 24 my ($lbi) = @_;
1106 10         23 my $ip = $lbi->get_curip;
1107              
1108             # Fetching fingerprint.
1109 10         34 my $count = $ip->spop;
1110 10         19 my $fgrprt = 0;
1111 10         33 while ( $count-- > 0 ) {
1112 16         42 my $val = $ip->spop;
1113 16 100       42 $lbi->abort( "Attempt to build a fingerprint with a negative number" )
1114             if $val < 0;
1115 15         47 $fgrprt = $fgrprt * 256 + $val;
1116             }
1117              
1118             # Transform the fingerprint into a library name.
1119 9         14 my $lib = "";
1120 9         13 my $finger = $fgrprt;
1121 9         22 while ( $finger > 0 ) {
1122 35         43 my $c = $finger % 0x100;
1123 35         39 $lib .= chr($c);
1124 35         76 $finger = int ( $finger / 0x100 );
1125             }
1126 9         27 $lib = "Language::Befunge::lib::" . reverse $lib;
1127              
1128             # Checking if library exists.
1129 9         659 eval "require $lib";
1130 9 100       37 if ( $@ ) {
1131 1         7 debug( sprintf("unknown extension $lib (0x%x): reversing\n", $fgrprt) );
1132 1         4 $ip->dir_reverse;
1133             } else {
1134             # Unload the library.
1135 8         60 debug( sprintf("unloading library $lib (0x%x)\n", $fgrprt) );
1136 8         36 $ip->unload($lib);
1137             }
1138             }
1139              
1140             =item lib_run_instruction( )
1141              
1142             =cut
1143              
1144             sub lib_run_instruction {
1145 13     13 1 27 my ($lbi) = @_;
1146 13         42 my $ip = $lbi->get_curip;
1147 13         62 my $char = $lbi->get_storage->get_char( $ip->get_position );
1148              
1149             # Maybe a library semantics.
1150 13         38 debug( "library semantics\n" );
1151 13         36 my $stack = $ip->get_libs->{$char};
1152              
1153 13 50       36 if ( scalar @$stack ) {
1154 13         24 my $obj = $stack->[-1];
1155 13         118 debug( "library semantics processed by ".ref($obj)."\n" );
1156 13         55 $obj->$char( $lbi );
1157             } else {
1158             # Non-overloaded capitals default to reverse.
1159 0           debug("no library semantics found: reversing\n");
1160 0           $ip->dir_reverse;
1161             }
1162             }
1163              
1164             =back
1165              
1166             =cut
1167              
1168             1;
1169              
1170             __END__