RubyでSchemeを作ってみたよ今度こそ。

ちゃんとREPLでインタープリット!末尾再帰最適化!継続なし!(でも原理的にはすぐ実装できるはず……!)中間言語方式!

class Cons
  def initialize(a,d)
    @car=a
    @cdr=d
  end
  attr_accessor :car
  attr_accessor :cdr
  def cadr
    cdr.car
  end
  def caddr
    cdr.cadr
  end
  def cadddr
    cdr.caddr
  end
  def cddr
    cdr.cdr
  end
  def to_a
    return [car] if cdr.nil?
    return [car] + cdr.to_a if cons? cdr
    raise 'not proper list'
  end
  def length
    return 1 if cdr.nil?
    return 1 + cdr.length
    raise 'not proper list'
  end
  def to_s
    str="("
    cur=self
    while cons? cur.cdr
      str+=cur.car.to_s + " "
      cur=cur.cdr
    end
    str+=cur.car.to_s
    str+=" . " +cur.cdr.to_s unless cur.cdr.nil?
    str+=")"
    return str
  end
end

def nil.to_s
  "()"
end

def list(*ss)
  return nil if ss.length==0
  return cons(ss.first,list(*ss[1...ss.length]))
end

def cons?(s)
  s.kind_of?(Cons)
end

def cons(car,cdr)
  return Cons.new(car,cdr)
end

class Environment
  def initialize(parent)
    @parent=parent
    @hash=Hash.new
  end
  attr_reader :parent
  def key?(name)
    return current_key?(name) || (@parent && @parent.key?(name))
  end
  def current_key?(name)
    return @hash.key?(name)
  end
  def define(name,value)
    @hash.store(name,value)
  end
  def get(name)
    return @hash[name] if current_key? name
    return @parent.get(name) unless @parent.nil?
    raise "not defined: #{name}"
  end
  def set(name,value)
    return @hash.store(name,value) if current_key?(name)
    return @parent.set(name,value) unless @parent.nil?
    raise "not defined: #{name}"
  end
end


def parse_s(src)
  src=src.gsub(/\s+/,",").gsub(/[a-zA-Z+\-*\/][a-zA-Z0-9?!]*/,":\\0").gsub("(","list(")
  eval(src)
end


Frame=Struct.new(:parent,:env,:nxt,:rib)
Closure=Struct.new(:env,:params,:body)

def compile(sexpr,nxt)
  #p "compile #{sexpr.to_s},#{nxt.to_s}"
  return compile_list(sexpr,nxt) if cons? sexpr
  return [:refer, sexpr, nxt] if sexpr.kind_of? Symbol
  return [:const, sexpr, nxt]
end

def compile_list(sexpr,nxt)
  case sexpr.car
  when :if
    th=compile(sexpr.caddr,nxt)
    el=compile(sexpr.cadddr,nxt)
    return compile(sexpr.cadr,[:test,th,el])
  when :lambda
    body=sexpr.cddr.to_a.reverse.inject([:return]){|r,i|compile(i,r)}
    params=sexpr.cadr.to_a
    return [:close,params,body,nxt]
  when :define
    return compile(sexpr.caddr,[:define,sexpr.cadr,nxt])
  when :set!
    return compile(sexpr.caddr,[:set,sexpr.cadr,nxt])
  else
    call=sexpr.cdr.to_a.reverse.inject(compile(sexpr.car,[:apply])){|r,i|compile(i,[:param,r])}
    call=[:frame,nxt,call] unless nxt[0]==:return
    return call
  end
end



class VM
  attr_reader :env
  def initialize env
    @global=env
    @env=Environment.new(env)
    @next=[:halt]
    @ax=nil
    @rib=[]
    @frame=nil
  end
  def run_string(src)
    run(compile(parse_s(src),[:halt]))
  end
  def run(instruction)
    @next=instruction
    while true
      case @next[0]
      when :halt
        return @ax
      when :refer
        @ax=@env.get(@next[1])
        @next=@next[2]
      when :const
        @ax=@next[1]
        @next=@next[2]
      when :test
        if @ax.nil?
          @next=@next[2]
        else
          @next=@next[1]
        end
      when :close
        @ax=Closure.new(@env,@next[1],@next[2])
        @next=@next[3]
      when :apply
        if @ax.instance_of? Closure
          @env=Environment.new(@ax.env)
          @rib.each_index{|i|
            @env.define(@ax.params[i],@rib[i])
          }
          @rib=[]
          @next=@ax.body
        elsif @ax.instance_of? Proc
          @ax=@ax.call *@rib
          @next=[:return]
        end
      when :frame
        @frame=Frame.new(@frame,@env,@next[1],@rib)
        @rib=[]
        @next=@next[2]
      when :param
        @rib.push @ax
        @next=@next[1]
      when :return
        @env=@frame.env
        @next=@frame.nxt
        @rib=@frame.rib
        @frame=@frame.parent
      when :define
        @global.define(@next[1],@ax)
        @next=@next[2]
      when :set
        @env.set(@next[1],@ax)
        @next=@next[2]
      else
        raise 'unknown instruction'
      end
    end
  end
end


def repl
  global=Environment.new(nil)
  global.define(:nil,nil)
  global.define(:list,lambda{|*s|list *s})
  global.define(:+,lambda{|*s| s.inject(0){|r,i|r+i}})
  global.define(:read,lambda{parse_s gets.chop})
  global.define(:eval,lambda{|s|VM.new(global).run(compile(s,[:halt]))})
  global.define(:print,lambda{|s|puts s.to_s})
  global.define(:repl,Closure.new(Environment.new(global),[],compile(parse_s('((lambda () (print (eval (read))) (repl)))'),[:halt])))
  vm=VM.new global
  vm.run_string '(repl)'
end
(define incr nil)

;定番のアレ
((lambda (counter) (set! incr (lambda () (set! counter (+ counter 1))))) 0) 

(incr)
1
(incr)
2
(incr)
3


(define inf (lambda () (inf))) ;無限に実行できるよ……!なぜなら末尾再帰だから……!
(define inf2(lambda () (inf) 1)) ;末尾再帰じゃないとメモリをモリモリ食べた挙句落ちるよ……!