配列ベースプログラミング言語「ざぼん」

LispにS式があってマクロがあるように。
Rubyの配列を使ってプログラミング言語を書いてみました。Ruby本体を使ってプログラム(= 配列)を操作することで、Lispのマクロのようなことができます。言語の名前は「ざぼん」です。「ざ」をいただきました。
とりあえずフィボナッチを求めるプログラムを書くための最低限を実装しています。プログラムは関数定義のみで、main関数がエントリポイントになっています。で、main関数の返り値を表示します。細かい文法はLispから直感を働かせてください。
ファイル: zbn.rb

#!/usr/bin/env ruby
# -*- compile-command: "ruby zbn.rb" -*-

class Env
  def initialize(parent)
    @parent = parent
    @env = {}
  end
  attr_reader :parent, :env
  protected   :parent, :env
  
  def []=(sym, val)
    @env[sym] = val
  end
  
  def [](sym)
    env = self
    while env
      val = env.env[sym]
      return val if val
      env = env.parent
    end
    nil
  end
end

class RootEnv < Env
  def initialize
    super(nil)
    self[:+] = [[:a, :b], lambda {|env| env[:a] + env[:b]}]
    self[:-] = [[:a, :b], lambda {|env| env[:a] - env[:b]}]
    self[:*] = [[:a, :b], lambda {|env| env[:a] * env[:b]}]
    self[:/] = [[:a, :b], lambda {|env| env[:a] / env[:b]}]

    self[:eq] = [[:a, :b], lambda {|env| env[:a] == env[:b]}]
  end
end

def ebal(env, body)
  case body
  when Proc
    body[env]
  when Array
    case body.first
    when :if
      ebal(env, body[1]) ? ebal(env, body[2]) : ebal(env, body[3])
    else
      func, *args = body.map {|a| ebal(env, a)}
      newenv = Env.new(env)
      func[0].each_with_index do |sym, i|
        newenv[sym] = args[i]
      end
      ebal(newenv, func[1])
    end
  when Symbol
    env[body]
  else
    body
  end
end

def parse(env, program)
  program.each do |expr|
    func, *arg_and_body = expr
    env[func] = arg_and_body
  end
end

if $0 == __FILE__
  env = RootEnv.new
  parse(env, eval(ARGF.read))
  p ebal(env, env[:main][1])
end

実行例そのいち。階乗。
ファイル: fact.zbn

# -*- ruby -*-
[
  [:fact, [:n],
    [:if, [:eq, :n, 0],
      1,
      [:*, :n, [:fact, [:-, :n, 1]]]]],
  
  [:main, [], [:fact, 3]]
]
$ ruby zbn.rb fact.zbn
6

実行例そのに。フィボナッチ。
ファイル: fib1.zbn

# -*- ruby -*-
[
  [:fib, [:n],
    [:if, [:eq, :n, 0],
      1,
      [:if, [:eq, :n, 1],
        1,
        [:+, [:fib, [:-, :n, 1]], [:fib, [:-, :n, 2]]]]]],
  
  [:main, [], [:fib, 10]]
]
$ ruby zbn.rb fib1.zbn
89

実行例そのさん。フィボナッチ。condマクロ版。
ファイル: fib2.zbn

# -*- ruby -*-

def cond(*exprs)
  root = list = nil
  exprs.each do |expr|
    ifexpr = [:if, *expr]
    list << ifexpr if list
    list = ifexpr
    root = list unless root
  end
  list << nil if list
  root
end

[
  [:fib, [:n],
    cond(
      [[:eq, :n, 0], 1],
      [[:eq, :n, 1], 1],
      [true, [:+, [:fib, [:-, :n, 1]], [:fib, [:-, :n, 2]]]])],
  
  [:main, [], [:fib, 10]]
]
$ ruby zbn.rb fib2.zbn
89

いかがでしょうか? Rubyで配列操作してプログラムをいじれるってのがポイントです。いろいろ失った代わりに自由を得ました。
参照: Route 477