Unlambdaインタプリタ by Ruby

Schemeインタプリタに行き詰まってきたので、とりあえず超難解プログラミング言語、Unlambdaのインタプリタを作っちゃいました。
http://hw001.gate01.com/eggplant/tcf/unlambda/

たぶん、それなりに動くものが出来たと思います。
まあ、俺自身Unlambdaのプログラムが書けないのに、ちゃんと動くっぽいってのも変だけど。
そういや、Rubyでプログラム書いたの久しぶりかもしれないな。


次はWhitespaceのインタプリタを作ろう!

class Proc
  @delay
  attr_accessor :delay
end

class UnlambdaInterpriter
  def initialize
    @cur = nil
    @i = make_function("i")
    @v = make_function("v")
  end

  def read_delay_block(chars )
    cmd = next_command(chars)
    if cmd == "`"
      cmd + read_delay_block(chars) +
         read_delay_block(chars)
    else
      cmd
    end
  end

  def next_command(chars)
    if chars.empty?
      return nil
    end

    ret = nil
    op = chars.shift
    if op == "." or op == "?"
      unless chars.empty?
        ret = op + chars.shift
      end
    elsif op == "#"
      while chars.shift != "\n"
        # skip
      end
      ret = next_command(chars)
    elsif op =~ /\s/
      ret = next_command(chars)
    else
      ret = op
    end
    return ret
  end

  def _eval(chars)
    #p chars
    case c = next_command(chars)
    when "`"
      op = _eval(chars)
      if op.delay
        op.delay = false
        return op
      else
        arg, dummy = _eval(chars)
        return op[arg]
      end
    when nil
      raise "nil !"
    else
      return make_function(c, chars)
    end
  end

  def make_function(c, chars=[])
    ret = nil
    case c
    when "i"
      return proc{|x|
        x
      }
    when "k"
      return proc{|a|
        proc{|b|
          a
        }
      }
    when "s"
      return proc{|a|
        proc{|b|
          proc{|c|
            a[c][b[c]]
          }
        }
      }
    when /\A\.(.)\Z/
      return proc{|x|
        print $1
        x
      }
    when "r"
      return proc{|x|
        print "\n"
        x
      }
    when "e"
      return proc{|x|
        exit
      }
    when "d"
      delay_str = read_delay_block(chars)
      ret = proc{|a|
        #p :delaycalled
        self.eval(delay_str)
      }
      ret.delay = true
      return ret
    when "v"
      ret = proc{|v|
        ret
      }
      return ret
    when "@"
      return proc{|x|
        if STDIN.eof?
          x[@v]
        else
          @cur = STDIN.getc.chr
          x[@i]
        end
      }
    when /\A\?(.)\Z/
      return proc{|x|
        if $1 == @cur
          x[@i]
        else
          x[@v]
        end
      }
    when "|"
      return proc{|x|
        if @cur
          print @cur
          x[@i]
        else
          x[@v]
        end
      }
    when "c"
      return proc{|x|
        callcc{|c|
          x[
            proc{|y|
              c.call(y)
            }
          ]
        }
      }
    else
      return _eval(chars)
    end
  end
  
  def eval(str)
    #p str
    chars = str.unpack("c*").map{|e|e.chr}
    _eval(chars)
  end
end

if __FILE__ == $0
  # CTRL-Cで止める
  str = <<-EOT
    ```s``s``sii`ki
    `k.*``s``s`ks 
    ``s`k`s`ks``s``s`ks``s`k`s`kr``s`k`sikk
    `k``s`ksk
  EOT

  #str = "````skk.ai"
  #str = "`c``s`kr``si`ki"
  #str = "`d`ri"
  #str = "``d`.aii"
  #str = "````.a.b.c.di"
  #str = "`.a`vi"
  #str = "`|`@i"

  intp = UnlambdaInterpriter.new
  intp.eval(str)
end