Advertisement
HwapX

Brainfuck Compiler (*.com)

Dec 19th, 2012
173
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 7.43 KB | None | 0 0
  1. package Brainfuck::Compiler;
  2.  
  3. use 5.014;
  4. use strict;
  5. use warnings;
  6.  
  7. my @valid_ops = my ($op_plus, $op_minus, $op_next, $op_previus, $op_begin, $op_end, $op_put, $op_get) = map(ord, ('+', '-', '>', '<', '[', ']', '.', ','));
  8. my @optimizable_ops = ($op_plus, $op_minus, $op_next, $op_previus);
  9.  
  10. #my $cell_max = 255;
  11. my %_container = (size => 0x1000,
  12.                   base => 0xFFFF - 0x1000);
  13.                         # 0xFFFF = max addressing
  14.                         # 0x1000 = container size
  15. my %_cell      = (max  => 0xFF,
  16.                   min  => 0x00);
  17.  
  18. sub new {
  19.     my $class = shift;
  20.     return bless {}, $class;
  21. }
  22.  
  23. sub compile {
  24.     my ($self, $c) = @_;
  25.     my @cb = map(ord, split(//, $c));
  26.     my @cc = $self->strip_comments(\@cb);
  27.     my @oc = $self->optimize(\@cc);
  28.     my @bt = $self->build(\@oc);
  29.     my $bin = $self->write(\@bt);
  30.     return $bin;
  31. }
  32.  
  33. sub optimize {
  34.     my ($self, $cc) = @_;
  35.     my @oc;
  36.     my $cs = scalar @{$cc};
  37.    
  38.     for(my $cp = 0; $cp < $cs; $cp++) {
  39.         my $op = $cc->[$cp];
  40.         my $opc = 0;
  41.        
  42.         if($self->can_optimize($op)) {
  43.             while($cc->[$cp] == $op) {
  44.                 $opc++;
  45.                 $cp++;
  46.                 # fix: this is only effective with + and -
  47.                 if($opc > $_cell{'max'}) {
  48.                     $opc = 0;
  49.                 }
  50.                 if($cp == $cs) {
  51.                     last;
  52.                 }
  53.             }
  54.             $cp--;
  55.         }
  56.         push(@oc, {op => $op, count => $opc});
  57.     }
  58.    
  59.     return @oc;
  60. }
  61.  
  62. # return a little-endian unsigned short int binary representation of a number
  63. sub _binShort {
  64.     my $value = shift;
  65.    
  66.     return pack("s", $value);
  67. }
  68.  
  69. # return a unsigned byte binary representation of a number
  70. sub _binByte {
  71.     my $value = shift;
  72.    
  73.     return pack("C", $value);
  74. }
  75.  
  76. # this will fill all cells with 0 and set DI register to 0
  77. sub _startup {
  78.     #Intel 8086 Assembly code
  79.     return "\xBB". _binShort($_container{"base"}).
  80.           # mov bx, $container_base
  81.            "\xB9". _binShort($_container{"size"}).
  82.           # mov cx, $container_size
  83.           # loop_start:
  84.            "\xC6\x07\x00".
  85.           # mov [bx], 0
  86.            "\x43".
  87.           # inc bx
  88.            "\xE2\xFA".
  89.           # loop loop_start
  90.            "\xBF\x00\x00";
  91.           # mov di, 0
  92.  
  93.     # Same code in Perl
  94.     # $bx = $container_base
  95.     # for $cx (0..$container_size) {
  96.     #     MEM[$bx] = 0;
  97.     #     $bx++;
  98.     # }
  99.     # $di = 0;
  100. }
  101.  
  102. sub _incPtr {
  103.     my($value) = shift;
  104.    
  105.     #Intel 8086 Assembly code
  106.     return "\x81\xC7". _binShort($value).
  107.           # add di, $value
  108.            "\x81\xFF". _binShort($_container{"size"}).
  109.           # cmp di, $container_size
  110.            "\x7E\x05".
  111.           # jle less_or_qual
  112.            "\x81\xEF". _binShort($_container{"size"}).
  113.           # sub di, $container_size
  114.            "\x4F";
  115.           # dec di
  116.           # less_or_equal
  117.  
  118.     # Same code in Perl
  119.     # $di+= $value
  120.     # if($di > $container_size) {
  121.     #     $di-= $container_size;
  122.     #     $di--;
  123.     # }
  124. }
  125.  
  126. sub _decPtr {
  127.     my($value) = shift;
  128.    
  129.     return "\x81\xEF". _binShort($value).
  130.           # sub di, $value
  131.            "\x73\x09".
  132.           # jnc no_underflow
  133.            "\xF7\xD7".
  134.           # not di
  135.            "\xBB". _binShort($_container{"size"}).
  136.           # mov bx, $container_size
  137.            "\x2B\xDF".
  138.           # sub bx, di
  139.            "\x8B\xFB";
  140.           # mov di, bx
  141.           # no_underflow:
  142.    
  143.     # Same code in Perl
  144.     # $di-= $value
  145.     # TODO: terminar
  146. }
  147.  
  148. sub _incCell {
  149.     my($value) = shift;
  150.    
  151.     return "\x80\x85". _binShort($_container{"base"}). _binByte($value);
  152.           # add [di + $container_base], $value
  153.    
  154.     # Same code in Perl
  155.     # MEM[$di + $container_base]+= $value
  156. }
  157.  
  158. sub _decCell {
  159.     my($value) = shift;
  160.    
  161.     return "\x80\xAD". _binShort($_container{"base"}). _binByte($value);
  162.           # sub [di + $container_base], $value
  163.    
  164.     # Same code in Perl
  165.     # MEM[$di + $container_base]-= $value
  166. }
  167.  
  168. sub _printCell {
  169.     my $value = shift;
  170.    
  171.     return "\xB4\x02".
  172.           # mov ah, 2
  173.            "\x8A\x95". _binShort($_container{"base"}).
  174.           # mov dl, [di + $container_base]
  175.            "\xCD\x21";
  176.           # int 21h
  177.    
  178.     # Same code in Perl
  179.     #
  180. }
  181.  
  182. sub _readInCell {
  183.     return "\xB4\x01".
  184.           # mov ah, 1
  185.            "\xCD\x21".
  186.           # int 21h
  187.            "\x88\x85". _binShort($_container{"base"});
  188.           # mov [di + $container_base], al
  189.  
  190.     # Same code in Perl
  191.     #
  192. }
  193.  
  194. sub _beginLoop {
  195.     my $end = shift;
  196.    
  197.     return "\x80\xBD". _binShort($_container{"base"}). "\x00".
  198.           # cmp [di + $container_base], 0
  199.            "\x75\x03".
  200.           # jne hack
  201.            "\xE9". _binShort($end);
  202.           # jmp $end
  203.           # hack
  204. }
  205.  
  206. sub _endLoop {
  207.     my $start = shift;
  208.     return "\xE9". _binShort($start);
  209.           # jmp $start
  210. }
  211.  
  212. sub build {
  213.     my ($self, $oc) = @_;
  214.     my $ocs = scalar @{$oc};
  215.     my @bt;
  216.     my $bin_size = 0;
  217.    
  218.     for(my $i = 0; $i < $ocs; $i++) {
  219.         my $oop = $oc->[$i];
  220.         my $inst = '';
  221.        
  222.         given($oop->{'op'}) {
  223.             when ($op_plus) {
  224.                 $inst = _incCell($oop->{"count"});
  225.             }
  226.             when ($op_minus) {
  227.                 $inst = _decCell($oop->{"count"});
  228.             }
  229.             when ($op_next) {
  230.                 $inst = _incPtr($oop->{"count"});
  231.             }
  232.             when ($op_previus) {
  233.                 $inst = _decPtr($oop->{"count"});
  234.             }
  235.             when ($op_put) {
  236.                 $inst = _printCell();
  237.             }
  238.             when ($op_get) {
  239.                 $inst = _readInCell();
  240.             }
  241.             when ($op_begin) {
  242.                 $inst = _beginLoop(0);
  243.             }
  244.             when ($op_end) {
  245.                 $inst = _endLoop(0);
  246.             }
  247.         }
  248.         $bin_size+= length $inst;
  249.         push(@bt, {op => $oop->{'op'}, instruction => $inst, offset => $bin_size});
  250.     }
  251.    
  252.     sub find_end {
  253.         my $from = shift() + 1;
  254.         my @bt = @_;
  255.         my $skip = 0;
  256.        
  257.         for my $i($from..$#bt) {
  258.             given($bt[$i]->{'op'}) {
  259.                 when ($op_begin) {
  260.                     $skip++;
  261.                 }
  262.                 when ($op_end) {
  263.                     return $i if(!$skip);
  264.                     $skip--;
  265.                 }
  266.             }
  267.         }
  268.     }
  269.    
  270.     for my $i(0..$#bt) {
  271.         if($bt[$i]->{'op'} == $op_begin) {
  272.             my $end = find_end($i, @bt);
  273.             $bt[$i]->{'instruction'} = _beginLoop($bt[$end]->{'offset'} - $bt[$i]->{'offset'});
  274.             $bt[$end]->{'instruction'} = _endLoop(-($bt[$end]->{'offset'} - ($bt[$i]->{'offset'} - length($bt[$i]->{'instruction'}))));
  275.         }
  276.     }
  277.    
  278.     return @bt;
  279. }
  280.  
  281. sub write {
  282.     my ($self, $bt) = @_;
  283.     my $bts = scalar @{$bt};
  284.     my $bin = _startup();#"\xBF\x00\x00"; # mov DI, 0
  285.    
  286.     for(my $i = 0; $i < $bts; $i++) {
  287.         my $it = $bt->[$i];
  288.         $bin .= $it->{'instruction'};
  289.     }
  290.     $bin .= "\xC3"; # ret
  291.     return $bin;
  292. }
  293.  
  294. sub strip_comments {
  295.     my ($self, $c) = @_;
  296.     return grep {$self->is_valid_op($_)} @{$c};
  297. }
  298.  
  299. sub can_optimize {
  300.     my ($self, $op) = @_;
  301.    
  302.     return $op~~@optimizable_ops;
  303. }
  304.  
  305. sub is_valid_op {
  306.     my ($self, $op) = @_;
  307.  
  308.     return $op~~@valid_ops;
  309. }
  310.  
  311. 1;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement