西尾泰和さんの最もタメになる「初心者用言語」は 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 じゃないですよね。