Unlambda インタプリタを書いてみた

西尾泰和さんの最もタメになる「初心者用言語」は Unlambda!を見て、unlambda の初心者になるためにインタプリタRuby 1.9 で書いてみました。

d (delay) と入出力がなければとてもシンプル。各命令を定義通りに Proc に変換するだけ。いやあ、-> 記法はキモイですね。いつか慣れるのかな。

require "continuation"

def unlambda_subset(s)
  s = s.split(//)
  t = ->() do
    case s.shift
    when ?`; t[][t[]]
    when ?s; ->(f){ ->(g){ ->(x){ f[x][g[x]] } } }
    when ?k; ->(x){ ->(y){ x } }
    when ?i; ->(x){ x }
    when ?c; ->(f){ callcc{|c| f[c] } }
    when ?v; v = ->(x){ v }
    when ?e; ->(x){ exit }
    else t[]
    end
  end
  t[]
end

c (callcc) はそのまま Ruby の callcc に投げるだけ。あは。西尾さんによる Python での実装では、CPS 変換してるみたいですね。やっぱ callcc のない言語はダメですよ。
全命令を実装したバージョンはこちら。delay は基本通り proc を一皮かぶせる感じ。でも正しいかどうかはちょっと自信ない。あと unlambda の入出力命令はセンスないなあ。やっぱ時代は Lazy-K か。

require "continuation"

def unlambda(s)
  s = s.split(//)
  b = nil
  v = ->(x){ v }
  t = ->() do
    case s.shift
    when ?`; f, g = t[], t[]; ->(){ (h = f[]) ? h[g[]] : ->(x){ g[][x] } }
    when ?s; ->(){ ->(f){ ->(g){ ->(x){ f[x][g[x]] } } } }
    when ?k; ->(){ ->(x){ ->(y){ x } } }
    when ?i; ->(){ ->(x){ x } }
    when ?d; ->(){ nil }
    when ?c; ->(){ ->(f){ callcc{|c| f[c] } } }
    when ?v; ->(){ v }
    when ?r; ->(){ ->(x){ puts; x } }
    when ?.; c = s.shift; ->(){ ->(x){ print c; x } }
    when ?@; ->(){ ->(x){ b = $stdin.getc; x[b ? ->(x){ x } : v] } }
    when ??; c = s.shift; ->(){ ->(x){ x[b == c ? ->(x){ x } : v] } }
    when ?|; ->(){ ->(x){ d = b; x[b ? (putc b; ->(x){ print d; x }) : v] } }
    when ?e; ->(){ ->(x){ exit } }
    else t[]
    end
  end
  t[][]
end

こうやって実行します。

unlambda <<END
```s``sii`ki
 ``s``s`ks
     ``s``s`ks``s`k`s`kr
               ``s`k`si``s`k`s`k
                               `d````````````.H.e.l.l.o.,. .w.o.r.l.d.!
                        k
      k
  `k``s``s`ksk`k.*
END

unlambda に比べると brainfuck や befunge なんて全然 esoteric じゃないですよね。