#! /usr/bin/env perl use strict; use warnings; # use: # cat some_code.sfe | ./sfe.pl convert # cat some_code.sfe | ./sfe.pl run # # the interpreter doesn't do any input, since the program comes from STDIN # and there's no support for a separator # # conversion: # # + == +01{next}< # - == +ff{next}< # < == < # > == +00{next} # [foo] == +00{addr2}< |1 conv(foo) ?{addr1} +00{addr2} |2 < # . == . # , == , # # this crummy code uses 4-digit addresses and breaks down for long progs # increment this if necessary: my $ADDRLEN = 4; sub addr { return sprintf "%0${ADDRLEN}x", shift; } # read brainfuck from stdin my $code = ''; $code .= $_ while ; $code =~ s/[^.,<>\[\]+-]//g; # convert code sub conv { my ($code, $base) = @_; my ($result, $at) = ('', $base); while ($code =~ m{ \G ((?: (?>[^\[\]]+) | \[(?1)*\] )) }gx) { my $block = $1; if ($block =~ /^\[(.*?)\]$/) { # a loop my $loopat = $at + 4 + $ADDRLEN; my $loop = conv($1, $loopat); my $nextat = $loopat + length($loop) + 1 + $ADDRLEN + 3 + $ADDRLEN; $result .= "+00".addr($nextat)."<" . $loop . "?".addr($loopat) . "+00".addr($nextat) . "<"; $at = $nextat+1; } else { # something with no loops foreach my $op (split //, $block) { if ($op =~ /[<.,]/) { # no change necessary $result .= $op; $at++; } else { # +, -, >: convert to +xx, opt < my $arg = ($op ne '+' ? $op ne '-' ? '00' : 'ff' : '01'); $result .= "+".$arg.addr($at + 3 + $ADDRLEN); $at += 3 + $ADDRLEN; next if $op eq '>'; $result .= "<"; $at++; } } } } return $result; } my $result = conv($code, 0); # output or interpret the resulting string my $op = $ARGV[0] ||= ''; if ($op eq 'convert') { print $result, "\n"; } elsif ($op eq 'run') { my @tape = map { 0 } (1 .. 65536); my $pointer = 0; my $ip = 0; while ($ip < length($result)) { my $codefrag = substr $result, $ip, $ADDRLEN+3; # long enough for any inst $codefrag =~ m/(^[<,.]|\?[0-9a-f]+|\+[0-9a-f]{3,})/ or die "NURR CONFUSED bad op: $codefrag"; my $op = $1; #print "RUNNING: op $op at $ip cell $pointer val $tape[$pointer]\n"; if ($op eq '<') { $ip++; $pointer = ($pointer - 1) % 65536; } elsif ($op eq ',') { $ip++; $tape[$pointer] = 0; } elsif ($op eq '.') { $ip++; print chr($tape[$pointer]); } elsif ($op =~ /^\?([0-9a-f]+)$/) { $ip += 1+length($1); $ip = hex($1) if $tape[$pointer] != 0; } elsif ($op =~ /^\+([0-9a-f]{2})([0-9a-f]+)/) { $ip += 3+length($2); my $val = $tape[$pointer] = ($tape[$pointer] + hex($1)) % 256; $ip = hex($2) if $val == 0; $pointer = ($pointer + 1) % 65536; } else { die "NURRR CONFUSED impossible: $op"; } } print "\n-- finished --\n"; } else { print "USAGE: cat code.sfe | $0 convert/run\n"; }