2013年6月8日土曜日

[Ruby][Scheme] Micro Schemeの実装(16) defineを実装

defineを実装した。
https://github.com/takeisa/uschemer2/tree/v0.02

define命令の追加


Virutal machineにdefine命令を追加した。

命令説明
(define var)aレジスタのオブジェクトをシンボルvarで束縛する。

define式のコンパイル

define式は
(define var val)
(define (func arg0...) body)
の2種類の形式に対応する。

対応するRubyコードは以下の通り。

elsif symbol == :define then
  if exp.cdr.car.is_a?(SSymbol) then
    # (define var val)
    var = exp.cdr.car
    val = exp.cdr.cdr.car
    compile(val, Define.new(var, next_op))
  elsif list?(exp.cdr.car) then
    # (define (func arg0...) body)
    var = exp.cdr.car.car
    args = exp.cdr.car.cdr
    body = exp.cdr.cdr.car
    val = SCons.new(SSymbol.new(:lambda), SCons.new(args, SCons.new(body)))
    compile(val, Define.new(var, next_op))
  else
    raise "define syntax error"
  end

2番目の形式の場合は、
(define (func arg0...) body)

(define func (lambda (arg0...) body))
に変換し、再度コンパイルする。


実行例

$ ruby uschemer.rb                 
> (define (add a b) (+ a b)) ←◆ここでadd関数を定義
#<Instruction::Closure:0x96d8c98
 @body=
  #<Instruction::Frame:0x96d8cc0
   @ret=#<Instruction::Return:0x96d8d88>,
   @x=
    #<Instruction::Refer:0x96d8cd4
     @var=#<SSymbol:0x96d8e3c @value=:a>,
     @x=
      #<Instruction::Argument:0x96d8ce8
       @x=
        #<Instruction::Refer:0x96d8cfc
         @var=#<SSymbol:0x96d8e14 @value=:b>,
         @x=
          #<Instruction::Argument:0x96d8d10
           @x=
            #<Instruction::Refer:0x96d8d60
             @var=#<SSymbol:0x96d8e64 @value=:+>,
             @x=#<Instruction::Apply:0x96d8d74>>>>>>>,
 @env=
  #<Env:0x96d96e8
   @binds=
    [#<VarBind:0x96d96d4
      @bind=
       {:+=>#<Proc:0x96d9648@uschemer.rb:53 (lambda)>,
        :-=>#<Proc:0x96d95f8@uschemer.rb:54 (lambda)>,
        :*=>#<Proc:0x96d9594@uschemer.rb:55 (lambda)>,
        :/=>#<Proc:0x96d9530@uschemer.rb:56 (lambda)>,
        :add=>#<Instruction::Closure:0x96d8c98 ...>}>]>, ←◆define時の環境を持っている
 @vars=
  #<SCons:0x96d8ef0
   @car=#<SSymbol:0x96d8edc @value=:a>,
   @cdr=
    #<SCons:0x96d8ec8
     @car=#<SSymbol:0x96d8ea0 @value=:b>,
     @cdr=#<SNilClass:0x927bba0>>>>
> (add 10 20) ←◆ここでadd関数呼び出し
#<SNumber:0x96c2df8 @value=30> ← ◆30が返ってきた。

define式の評価後は、Virtual machineのaレジスタにクロージャのコンパイル結果が格納されているため、
上記のような表示となる。
式をVirtual machineの命令に変換し、関数呼び出しで、それが実行される。
自分で実装して、実際に動かしてみると、なかなか感動的で、かなり楽しい。