File Coverage

blib/lib/Language/BF.pm
Criterion Covered Total %
statement 41 95 43.1
branch 5 32 15.6
condition 0 5 0.0
subroutine 9 24 37.5
pod 10 13 76.9
total 65 169 38.4


line stmt bran cond sub pod time code
1             package Language::BF;
2 1     1   36588 use 5.008001;
  1         4  
  1         88  
3 1     1   6 use strict;
  1         1  
  1         41  
4 1     1   4 use warnings;
  1         7  
  1         1902  
5             our $VERSION = sprintf "%d.%02d", q$Revision: 0.3 $ =~ /(\d+)/g;
6              
7             sub new($;$$) {
8 1     1 1 12 my $class = shift;
9 1         4 my $bf = bless {}, $class;
10 1 50       10 $bf->code(shift) if @_;
11 1 50       5 $bf->input(shift) if @_;
12 1         4 $bf;
13             }
14              
15             sub new_from_file {
16 0     0 0 0 my $bf = shift->new();
17 0 0       0 my $bfile = shift or die __PACKAGE__, "->new_from_file(filename)";
18 0 0       0 open my $fh, "<", $bfile or die "$bfile:$!";
19 0         0 my $src = do { local $/; <$fh> };
  0         0  
  0         0  
20 0         0 close $fh;
21 0         0 $bf->code($src);
22 0         0 $bf;
23             }
24              
25             sub reset($){
26 1     1 1 3 my $bf = shift;
27 1         3 ( $bf->{pc}, $bf->{sp} ) = ( 0, 0 );
28 1         5 ( $bf->{data}, $bf->{in}, $bf->{out} ) = ( [], [], [] );
29 1         2 $bf;
30             }
31              
32             sub code($$) {
33 1     1 1 5 my ( $bf, $code ) = @_;
34 1         7 $code =~ tr/<>+\-.,[]//cd;
35 1         63 $bf->{code} = [ split //, $code ];
36 1         16 my $coderef = $bf->compile;
37 1 50       5 warn $coderef unless ref $coderef;
38 1         6 $bf->{coderef} = $bf->compile;
39 1         6 $bf->reset;
40 1         37 $bf;
41             }
42             *parse = \&code;
43              
44             sub compile($){
45 2     2 0 5 my $bf = shift;
46 2         3 my $src = <<'EOS';
47             sub {
48             my (@data, @out) = ();
49             my $sp = 0;
50             EOS
51 2         4 for my $op ( @{ $bf->{code} } ) {
  2         7  
52 222         1059 $src .= {
53             '<' => '$sp--;',
54             '>' => '$sp++;',
55             '+' => '$data[$sp]++;',
56             '-' => '$data[$sp]--;',
57             '.' => 'push @out, $data[$sp];',
58             ',' => '$data[$sp] = shift @_;',
59             '[' => 'while($data[$sp]){',
60             ']' => '}',
61             }->{$op}
62             . "\n";
63             }
64 2         4 $src .= <<'EOS';
65             return @out
66             }
67             EOS
68 2         931 my $coderef = eval $src;
69 2 50       11 return $@ ? $@ : $coderef;
70             }
71              
72             sub run($;$){
73 1     1 1 5 my ($bf, $interpret) = shift;
74 1 50       6 if ($interpret){
75 0   0     0 $bf->step while ( $bf->{code}[ $bf->{pc} ] and $bf->{pc} >= 0 );
76             }else{
77 1         27 $bf->{out} = [ $bf->{coderef}($bf->{in}) ];
78             }
79             }
80              
81 0 0   0 0 0 sub debug { my $bf = shift; $bf->{debug} = shift if @_; $bf->{debug} };
  0         0  
  0         0  
82              
83             sub input($$){
84 0     0 1 0 my ($bf, $input) = @_;
85 0         0 $bf->{in} = [ split //, $input ];
86 0         0 $bf;
87             }
88              
89             sub output($){
90 1     1 1 9 my $bf = shift;
91 1         2 join '', map {chr} @{$bf->{out}};
  13         33  
  1         3  
92             }
93              
94             sub as_source($) {
95 0     0 1   my $bf = shift;
96 0           require B::Deparse;
97 0           B::Deparse->new()->coderef2text( $bf->{coderef} );
98             }
99              
100             sub as_perl($) {
101 0     0 1   'print map{chr} sub'. $_[0]->as_source
102             . '->(split//, do{local $/;my $s=<>})' . "\n";
103             }
104              
105             sub step($){
106 0     0 1   my $bf = shift;
107 0           my $op = $bf->{code}[ $bf->{pc} ];
108 0 0         $bf->{debug}
109             and warn sprintf "pc=%d, sp=%d, op=%s", $bf->{pc}, $bf->{sp}, $op;
110             {
111 0     0     '<' => sub { $bf->{sp} -= 1 },
112 0     0     '>' => sub { $bf->{sp} += 1 },
113 0     0     '+' => sub { $bf->{data}[ $bf->{sp} ]++ },
114 0     0     '-' => sub { $bf->{data}[ $bf->{sp} ]-- },
115 0     0     '.' => sub { push @{ $bf->{out} }, $bf->{data}[ $bf->{sp} ] },
  0            
116 0     0     ',' => sub { $bf->{data}[ $bf->{sp} ] = shift @{ $bf->{in} } },
  0            
117             '[' => sub {
118 0 0   0     return if $bf->{data}[ $bf->{sp} ];
119 0           my $nest = 1;
120 0           while ($nest) {
121 0           $bf->{pc} += 1;
122 0 0         $nest +=
    0          
123             $bf->{code}[ $bf->{pc} ] eq '[' ? +1
124             : $bf->{code}[ $bf->{pc} ] eq ']' ? -1
125             : 0;
126 0           die "matching ] not found!"
127 0 0         if $bf->{pc} > scalar @{ $bf->{code} };
128             }
129             },
130             ']' => sub {
131 0     0     my $nest = 1;
132 0           while ($nest) {
133 0           $bf->{pc} -= 1;
134 0 0         $nest -=
    0          
135             $bf->{code}[ $bf->{pc} ] eq '[' ? +1
136             : $bf->{code}[ $bf->{pc} ] eq ']' ? -1
137             : 0;
138 0 0         die "matching [ not found!"
139             if $bf->{pc} < 0;
140             }
141 0           $bf->{pc}--;
142             },
143 0           }->{$op}();
144 0           $bf->{pc}++;
145             }
146              
147             sub as_c($;$){
148 0     0 1   my $bf = shift;
149 0   0       my $datasize = shift || 65536;
150 0           my $src = <<"EOS";
151             int main(int argc, char **argv){
152             char data[$datasize];
153             int sp = 0;
154             EOS
155 0           for my $op ( @{ $bf->{code} } ) {
  0            
156 0           $src .= {
157             '<' => 'sp--;',
158             '>' => 'sp++;',
159             '+' => 'data[sp]++;',
160             '-' => 'data[sp]--;',
161             '.' => 'putchar(data[sp]);',
162             ',' => 'data[sp] = getchar();',
163             '[' => 'while(data[sp]){',
164             ']' => '}',
165             }->{$op}
166             . "\n";
167             }
168 0           $src .= <<'EOS';
169             }
170             EOS
171 0           return $src;
172             }
173              
174              
175             1;
176             __END__