challenge BFコンパイラー

「どう書く?」でまだ出ていないのが不思議なお題。それがBF処理系。 ここでは、BFで書かれたソースを、同じ言語に変換するコンパイラーを募集します。

私自身、すでにPerlとJavaScriptに関しては http://blog.livedoor.jp/dankogai/archives/50545151.html でやっているのですが、他の言語バージョンも是非見たいので。

Dan the Brainf.ucker

以下のようにonelinerで可能です。 ただしLanguage::BF 0.03が必要です。 CodeRepos経由 で、

  • svn co svn.coderepos.org/share/lang/perl/Language-BF
  • cd Language-BF/trunk
  • perl Makefile.PL
  • make install

するか、CPANにVersion 0.03が現れるのをお待ち下さい。

Dan the Brainf.cker

1
2
3
4
perl -MLanguage::BF \
  -e 'print Language::BF->new_from_file(shift)->as_perl' t/hello.bf \
  | perl
Hello World!

Posted feedbacks - Ruby

ひねりなし。
配列の値がbyte型を越える場合についてはとりあえず考慮しない方向で…
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class BF
  def compile(str)
    depth = 0
    code = []
    code << "mem = [0]"
    code << "ptr = 0"
    str.each_byte do |ch|   
      if ch == ?>
        code << "\t" * depth + "ptr += 1; mem[ptr] = 0 if ptr >= mem.size"
      elsif ch == ?<
        code << "\t" * depth + "(ptr == 0)? mem.unshift(0) : ptr -= 1"
      elsif ch == ?+
        code << "\t" * depth + "mem[ptr] += 1"
      elsif ch == ?-
        code << "\t" * depth + "mem[ptr] -= 1"
      elsif ch == ?.
        code << "\t" * depth + "putc(mem[ptr])"
      elsif ch == ?,
        code << "\t" * depth + "mem[ptr] = STDIN.getc"
      elsif ch == ?[
        code << "\t" * depth + "while(mem[ptr] != 0) do"
        depth += 1
      elsif ch == ?]
        depth -= 1
        code << "\t" * depth + "end"
      end
    end
   code.join "\n"
  end
end

eval BF.new.compile("++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.") # => Hello World!

これまたif文の羅列を置き換える方向で書き換えてみました。
Cと違ってHashが気軽に使えるのがうれしい。
next unless が使えるのもPerl Mongerとしてはうれしい。
    str.unpack("C*").map{|c| c.chr}.each do |ch|
は、単に{}とdo endを両方使ってみたかったから。
Dan the Occasional Rubyist
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class BF
  @@opcode = {
    '>' => "ptr += 1; mem[ptr] = 0 if ptr >= mem.size",
    '<' => "(ptr == 0)? mem.unshift(0) : ptr -= 1",
    '+' => "mem[ptr] += 1",
    '-' => "mem[ptr] -= 1",
    '.' => "putc(mem[ptr])",
    ',' => "mem[ptr] = STDIN.getc",
    '[' => "while(mem[ptr] != 0) do",
    ']' => "end"
  }
  def compile(str)
    code = ["mem = [0]", "ptr = 0"]
    str.unpack("C*").map{|c| c.chr}.each do |ch|
      next unless @@opcode[ch]
      code << @@opcode[ch]
    end
   code.join "\n"
  end
end

ポインタとメモリ領域を,文字列stackの中に置き,regexpなどでstackを操作しつつ動作するようなコードを吐きます.
例)
% cat hello.bf
++++++++++[>+++++++>++++++++++>+++>+<<<<-]
>++.>+.+++++++..+++.>++.<<+++++++++++++++.
>.+++.------.--------.>+.>.

% make
ruby bfcompile.rb < hello.bf > hello.rb
ruby hello.rb
Hello World!

% cat hello.rb
class String
  def until_nz
    while self[self[0] + 1] != 0
      replace(yield(self))
    end
    self
  end

  def refer
    self.sub(/\A(.)(.*)\Z/m) do
      $1 + $2.sub(/^(.{#{$1[0]}})(.)/m) do
        $1 + yield($2)
      end
    end
  end
end

stack = "\000" + "\000" * 256
stack.refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
until_nz {|stack|
stack.
sub(/./m) {|p| (p[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
sub(/./m) {|p| (p[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
sub(/./m) {|p| (p[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
sub(/./m) {|p| (p[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
sub(/./m) {|p| (p[0] - 1).chr }.
sub(/./m) {|p| (p[0] - 1).chr }.
sub(/./m) {|p| (p[0] - 1).chr }.
sub(/./m) {|p| (p[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
to_s
}.
sub(/./m) {|p| (p[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| putc(mem) }.
sub(/./m) {|p| (p[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| putc(mem) }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| putc(mem) }.
refer {|mem| putc(mem) }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| putc(mem) }.
sub(/./m) {|p| (p[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| putc(mem) }.
sub(/./m) {|p| (p[0] - 1).chr }.
sub(/./m) {|p| (p[0] - 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| putc(mem) }.
sub(/./m) {|p| (p[0] + 1).chr }.
refer {|mem| putc(mem) }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| putc(mem) }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| putc(mem) }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| (mem[0] - 1).chr }.
refer {|mem| putc(mem) }.
sub(/./m) {|p| (p[0] + 1).chr }.
refer {|mem| (mem[0] + 1).chr }.
refer {|mem| putc(mem) }.
sub(/./m) {|p| (p[0] + 1).chr }.
refer {|mem| putc(mem) }.
to_s
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
puts <<'EOS'
class String
  def until_nz
    while self[self[0] + 1] != 0
      replace(yield(self))
    end
    self
  end

  def refer
    self.sub(/\A(.)(.*)\Z/m) do
      $1 + $2.sub(/^(.{#{$1[0]}})(.)/m) do
        $1 + yield($2)
      end
    end
  end
end

stack = "\000" + "\000" * 256
EOS

print 'stack.'

code = {
  '>' => [ 'sub(/./m) {|p| (p[0] + 1).chr }.' ],
  '<' => [ 'sub(/./m) {|p| (p[0] - 1).chr }.' ],
  '+' => [ 'refer {|mem| (mem[0] + 1).chr }.' ],
  '-' => [ 'refer {|mem| (mem[0] - 1).chr }.' ],
  '.' => [ 'refer {|mem| putc(mem) }.' ],
  ',' => [ 'refer { STDIN.getc }.' ],
  '[' => [ 'until_nz {|stack|', 'stack.' ],
  ']' => [ 'to_s', '}.' ],
}
while ch = STDIN.getc
  next if code[ch.chr].nil?
  puts code[ch.chr]
end
puts 'to_s'

メモリとポインタを用意しておいて,あとは命令を1つずつRubyに置き換えるだけ。他の人と々アプローチだけど,いくらかわかりやすく書いたつもり。

書いてて気がついたけど,BFのループって終了判定が2カ所(前後)にあるんだな。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
print <<"EOBF"
class BF
  def initialize
    @memory = [0]
    @pointer = 0
  end
  def current
    @memory[@pointer]
  end
  def incr
    @memory[@pointer] += 1
  end
  def decr
    @memory[@pointer] -= 1
  end
  def right
    @pointer += 1
    unless @memory[@pointer]
      @memory << 0
    end
  end
  def left
    @pointer -= 1
  end
  def set(c)
    @memory[@pointer] = c
  end
  def printc
    print @memory[@pointer].chr
  end
end

bf = BF.new

EOBF

commands = {
  ">" => "bf.right",
  "<" => "bf.left",
  "+" => "bf.incr",
  "-" => "bf.decr",
  "." => "bf.printc",
  "," => "c = STDIN.getc; bf.set(c)",
  "[" => "begin; break if bf.current.zero?",
  "]" => "end until bf.current.zero?"
}
ARGF.each_byte do |c|
  cmd = commands[c.chr]
  puts cmd if cmd
end

Index

Feed

Other

Link

Pathtraq

loading...