Advertisement
Guest User

perl brainf*ck, 7 kb

a guest
Jan 12th, 2014
72
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 7.92 KB | None | 0 0
  1. #!/usr/bin/perl -w
  2. use strict;
  3.  
  4. sub escape_newline {
  5.     my $val = shift();
  6.     $val =~ s/\n/\\n/g;
  7.     return $val;
  8. }
  9.  
  10. my ($bf_code, $bf_input) = split(/!/, join("", <>), 2);
  11. printf("Bf code is:\n%s\nEND, length %d bytes.\n", $bf_code, length($bf_code));
  12.  
  13. printf("Bf input is:\n%s\nEND, length %d bytes.\n", $bf_input, length($bf_input));
  14.  
  15.  
  16. #While studying some existing brainfuck code, I've observed a few
  17. #common idioms, that look very easy to speed up by using static analysis.
  18. #Basically, any loop that contains balanced numbers of < and > characters,
  19. #could probably be statically analyzed.
  20.  
  21. #Storage.
  22. #  ( [offset, net_incr], [offset, net_incr] )
  23. my $bf_output = "";
  24.  
  25. my @s = ();
  26. my $p = 0;
  27. my $in_idx = 0;
  28.  
  29. my @j_table = ();
  30. my @add_table = ();
  31. my @s_table = ();
  32. my @s_table_baselen = ();
  33.  
  34. #Static anaylze time.
  35.  
  36. my $num_add_unique = 0;
  37. my $num_add_all = 0;
  38.  
  39. my $num_s_loops = 0;
  40. my $num_s_alllen = 0;
  41. for(my $i = 0; $i < length($bf_code); $i++){
  42.     my $firstchar = substr($bf_code, $i, 1);
  43.     if($firstchar eq "+" || $firstchar eq "-"){        
  44.         $num_add_unique++;
  45.         my $runlen = 1;
  46.         for(my $j = $i + 1; $j < length($bf_code) && substr($bf_code, $j, 1) eq $firstchar; $j++){
  47.             $runlen = $j - $i + 1;
  48.         }
  49.         $add_table[$i] = $runlen;
  50.         $num_add_all += $runlen;
  51.        
  52.         #overwrite with whitespace for clarity.
  53.         substr($bf_code, $i + 1, $runlen - 1, " " x ($runlen - 1));
  54.  
  55.         $i += $runlen - 1; #skip forward to next.        
  56.     }
  57.  
  58.     if($firstchar eq "["){
  59.         my $next_open = index($bf_code, "[", $i + 1);
  60.         my $next_close = index($bf_code, "]", $i + 1);
  61.  
  62.         #open before close, not simple.
  63.         #no open at all, simple.
  64.         if( $next_open == -1 || $next_close < $next_open){
  65.            
  66.             my $loop_inner = substr($bf_code, $i + 1, $next_close - $i - 1);
  67.             my $ok = $loop_inner =~ /^[<>+\-]*$/;
  68.             my $num_left = 0;
  69.             my $num_right = 0;
  70.             $loop_inner =~ s/(<)/$num_left++; $1/eg;
  71.             $loop_inner =~ s/(>)/$num_right++; $1/eg;
  72.  
  73.            
  74.             if($num_left == $num_right && $loop_inner =~ /^[<>+\s\-]*$/){
  75.                 # a simple loop
  76.                 $num_s_loops++;
  77.                 $num_s_alllen += length($loop_inner) + 2;
  78.                 $s_table_baselen[$i] = length($loop_inner) + 2;
  79.  
  80.                 printf("Found simple loop [%s]\n", escape_newline($loop_inner));
  81.                 my %unique_offsets = ();
  82.                 my $off_p = 0;
  83.                 for(my $k = 0; $k < length($loop_inner); $k++){
  84.                     my $li_char = substr($loop_inner, $k, 1);
  85.                     if($li_char eq "<"){
  86.                         $off_p--;
  87.                     }
  88.                     if($li_char eq ">"){
  89.                         $off_p++;
  90.                     }
  91.                     if($li_char eq "+"){
  92.                         $unique_offsets{$off_p}++;
  93.                     }
  94.                     if($li_char eq "-"){
  95.                         $unique_offsets{$off_p}--;
  96.                     }
  97.                 }
  98.                 my @s_loop_packed = ();
  99.                 print("  Result is (");
  100.                 for(sort({$a <=> $b} keys(%unique_offsets))){
  101.                     next if $_ == 0;
  102.                     push(@s_loop_packed, [$_, $unique_offsets{$_} ]);
  103.                     printf("[%d,%d], ", $_, $unique_offsets{$_});
  104.                 }
  105.                 print(")\n");
  106.                 if($unique_offsets{0} == -1){
  107.                     #everything's ok.
  108.                     $s_table[$i] = [@s_loop_packed];
  109.                     substr($bf_code, $i, 1, "S"); #to indicate start.
  110.                     substr($bf_code, $next_close, 1, "s"); #erase closing bracket.
  111.                     #overwrite with whitespace for clarity.
  112.                     substr($bf_code, $i + 1, length($loop_inner), " " x length($loop_inner));
  113.  
  114.                 }else{
  115.                     print("  Warning: exotic loop [$loop_inner].\n");
  116.                     #roll back changes made so far.
  117.                     $num_s_loops--;
  118.                     $num_s_alllen -= length($loop_inner) + 2;
  119.                     $s_table_baselen[$i] = undef;
  120.                 }
  121.  
  122.             }
  123.  
  124.            
  125.         }
  126.     }
  127. }
  128.  
  129. printf("Found %d runs of +/-, totalling %d +/-, average length %.2f\n",
  130.     $num_add_unique, $num_add_all, $num_add_all / $num_add_unique);
  131.  
  132. printf("Found %d simple loops, totalling %d bytes, average length %.2f\n",
  133.     $num_s_loops, $num_s_alllen, $num_s_alllen / $num_s_loops);
  134.  
  135. #Now print it out.
  136. print("Table (+/-) is:\n");
  137. for(my $idx = 0; $idx < $#add_table + 1; $idx++){
  138.     if($idx % 30 == 0){
  139.         print("  ");
  140.     }
  141.     print(" " . (defined($add_table[$idx]) ? $add_table[$idx] : " "));
  142.     if($idx % 30 == 29){
  143.         print("\n");
  144.     }
  145. }
  146. print("\n\n");
  147.  
  148. print("Table (s_loop_baselen) is:\n");
  149. for(my $idx = 0; $idx < $#s_table_baselen + 1; $idx++){
  150.     if($idx % 30 == 0){
  151.         print("  ");
  152.     }
  153.     print(" " . (defined($s_table_baselen[$idx]) ? $s_table_baselen[$idx] : " "));
  154.     if($idx % 30 == 29){
  155.         print("\n");
  156.     }
  157. }
  158. print("\n\n");
  159.  
  160. printf("After transformation, bf code is:\n%s\nEND, length %d bytes.\n", $bf_code, length($bf_code));
  161.  
  162. for (my $i = 0; $i < length($bf_code); $i++){
  163.     my $command = substr($bf_code, $i, 1);
  164.     if(! defined($s[$p])){
  165.         $s[$p] = 0;
  166.         print("Lengthening tape forwards by one.\n") if 0;
  167.     }
  168.     if($command eq "<"){
  169.         $p--;
  170.     }
  171.     if($command eq ">"){
  172.         $p++;
  173.     }
  174.     if($command eq "+"){
  175.         $s[$p] += $add_table[$i]; $s[$p] %= 256;
  176.         $i += $add_table[$i] - 1;
  177.     }
  178.     if($command eq "-"){
  179.         $s[$p] -= $add_table[$i]; $s[$p] %= 256;
  180.         $i += $add_table[$i] - 1;
  181.     }
  182.     if($command eq "["){
  183.         if($s[$p] == 0){
  184.             #jump forward.
  185.             my $orig_i = $i;
  186.             if(defined($j_table[$i])){
  187.                 $i = $j_table[$i];
  188.             }else{
  189.                 my $d = 1;
  190.                 while($d > 0 && $i < length($bf_code)){
  191.                     $i++;
  192.                     $d++ if substr($bf_code, $i, 1) eq "[";
  193.                     $d-- if substr($bf_code, $i, 1) eq "]";
  194.                 }
  195.                 $j_table[$orig_i] = $i;
  196.             }
  197.             printf("Jumping forward by %d bytes.\n", $i - $orig_i) if 0;
  198.         }
  199.     }
  200.     if($command eq "]"){
  201.         if($s[$p] != 0){
  202.             #jump backwards.
  203.             my $orig_i = $i;
  204.             if(defined($j_table[$i])){
  205.                 $i = $j_table[$i];
  206.             }else{
  207.                 my $d = 1;                
  208.                 while($d > 0 && $i > 0){
  209.                     $i--;
  210.                     $d++ if substr($bf_code, $i, 1) eq "]";
  211.                     $d-- if substr($bf_code, $i, 1) eq "[";
  212.                 }
  213.                 $j_table[$orig_i] = $i;
  214.             }
  215.             printf("Jumping backwards by %d bytes.\n", $orig_i - $i) if 0;
  216.         }
  217.     }
  218.     if($command eq ","){
  219.         print("Loading 1 byte of input.\n") if 0;
  220.         if($in_idx >  length($bf_input)){
  221.             last;
  222.         }
  223.         $s[$p] = ord(substr($bf_input, $in_idx++, 1));
  224.     }
  225.     if($command eq "."){
  226.         print("Outputing a single byte.\n") if 0;
  227.         $bf_output .= chr($s[$p]);
  228.     }
  229.     if($command eq "S"){
  230.         my $incrval = $s[$p];
  231.         my @to_incr = @{$s_table[$i]};
  232.         for my $spec (@to_incr){
  233.             (my $offset, my $incrmul) = @$spec;
  234.             $s[$p + $offset] += $incrmul * $incrval;
  235.             $s[$p + $offset] %= 256;
  236.         }
  237.  
  238.         $s[$p] = 0;
  239.         $i += $s_table_baselen[$i] - 1;
  240.     }
  241.     if($p == -1){
  242.         print("Lengthing tape backwards by one.\n") if 0;
  243.         $p = 0;
  244.         unshift(@s, 0);
  245.     }
  246. }
  247.  
  248.  
  249. printf("Bf code loaded %d bytes of input.\n", $in_idx);
  250. printf("Output of bf code was:\n%s\nEND, length %d bytes.\n", $bf_output, length($bf_output));
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement