`(Hello ,world)

ツッコミ、添削大歓迎です。いろいろ教えてください。

2012-07-14

3impのヒープベース処理系に多値を追加する

3imp処理系には多値がない。いままでどうやればいいのか思いつかなかったのだけど、なんとなくできたので。

compileのペアの評価に、多値を返すスペシャルフォーム values を追加する:

(define compile
  ...
  (record-case x
   ...
   (values args
           (recur loop ((args args)
                        (c (if (tail? next)
                               ;; Tail case: directly go to next command.      
                               (list 'values next)
                             ;; Non-tail case: implicit return.                
                             (list 'values '(return)))))
                  (if (null? args)
                      (if (tail? next)
                          c
                        (list 'frame next c))
                    (loop (cdr args)
                          (compile (car args)
                                   e
                                   (list 'argument c))))))
   ...

valuesがきたら、関数呼び出しと同じく引数をargumentで積んでいって、最後に (values next) というバイトコードを追加する。あとは末尾だったらframeを作らないとかは関数呼び出しと同じ。例:

> (compile '(values 1 2 3) '())
(constant 3 (argument (constant 2 (argument (constant 1 (argument (values (return))))))))

VMでは、オペコードがvaluesだったら、引数としてつまれている最初の値(空の場合はundefined)を a に代入する。それによって一番目の値をそのまま演算に使える。

(define VM
  ...
    (record-case x                                                            
     (values (x)
             (let1 a (if (null? r)
                         (undefined)
                       (car r))
                   (VM a x e r s r)))
  ...

多値を受け取る receive スペシャルフォームもコンパイラに追加する。receiveは (receive (x y z) (values 1 2 3) body) という形式:

(define compile
  ...
  (record-case x
   ...
   (receive (vars vals body)
            (compile vals e
                     (list 'receive
                           (compile body (extend e vars) next))))
   ...

まず vals をコンパイルして、あとはlambdaと同じく環境をvarsで拡張してボディを評価。

VMに、それまでのステートに加えて、多値用の変数 rest-values を追加する

(define VM
  (lambda (a x e r s rest-values)

バイトコードの receive がきたら、関数呼び出しの apply と同じような感じで、rest-values を使って環境を拡張する:

(define VM
  ...
    (record-case x                                                            
     (receive (x)
              (VM a x (extend e rest-values) '() s rest-values))
  ...

あとはvaluesで返した値の数がreceiveで受け取る数より少なかったり、restパラメータの処理とかは適宜。

ヒープベースには簡単に追加できるけど、スタックベースにも同様に追加できるでしょう。

Javaで作ってるやつのパッチこちら

トラックバック - http://cadr.g.hatena.ne.jp/mokehehe/20120714