A-normal form 再学習 その5 - Scheme VM

ここに書いてある内容は古いです。A正規形まとめをご参照ください。

以前通った道に戻ります。
前回は Core Scheme の A-reduction を理解しコードを書きました。次は現実の Scheme に対する A-reduction を考えます。基本的には Core Scheme と同じで良いはずですが考慮すべき点がいくつかあります。

let や lambda の body

Core Scheme と違い body には複数の式があることが許されます。例えば

(let1 a 3
  (+ (+ 1 2))
  (+ (+ 3 4)))

など。

これは Core Scheme からの類推で

(let1 a 2
  (let1 t1 (+ 1 2)
    (+ t1))
  (let1 t2 (+ 3 4)
    (+ t2)))

と normalize されるのが良いと考えました。

set!

set! は代入を行い、返す値は不定です。

(set! a (+ (+ 1 2)))
=> (let1 t1 (+ 1 2) (let1 t2 (+ t1) (set! a t2)))

と normalize するには意味がありそうです。(let1 が括りだされれるわけですから)

set-car!や set-cdr! なども同様。

define

set! と同様で良さそうです。

begin

(begin (+ (+ 1 2)) (+ (+ 3 4)))
=>
(begin (let1 t1 (+ 1 2) (+ t1)) (let1 t2 (+ 3 3) (+ t2)))

となるのが良さそうです。begin の中身が begin の外に出ると評価順序とかが難しくなりそうです。(ちょっと自信なし)

コード

(load "./lib/test.scm")

;;--------------------------------------------------------------------
;;
;; A-normalization for Scheme, not for Core Scheme.
;;
;;   Based on A linear time A-normalization alogrithm,
;;   in The Essence of Compiling with Continuations.
;;
;;
(define (a-normalize m)
  (a-normalize-with-k m (lambda (x) x)))

(define (a-normalize-with-k m k)
  (define (primitive-op? f)
    (eq? f '+))
  (match m
    ;; normalization of lambda is not in Figure 7, but Figure 9.
    [('lambda params . body)
     (k `(lambda
             ,params
             ,@(let loop ([body body] [nbody '()])
                 (if (null? body)
                     nbody
                     (append nbody (loop (cdr body) (list (a-normalize (car body)))))))))]
    [('let1 x m1 . m2*)
     (a-normalize-with-k m1 (lambda (normalized-m1)
                              (let loop ([body m2*]
                                         [m2n '()])
                                (if (null? body)
                                    `(let1 ,x , normalized-m1 ,@m2n)
                                    (loop (cdr body) (append m2n (list (a-normalize-with-k (car body) k))))))))]
;;     [('if m1 m2 m3)
;;      (a-normalize-with-k m1 (lambda (normalized-m1)
;;                               `(if ,normalized-m1
;;                                    ,(a-normalize-with-k m2 k)
;;                                    ,(a-normalize-with-k m3 k))))]
    ;; To prevent possible exponential growth in code size,
    ;; the algorithm avoids duplicating the evaluation context enclosing a conditional expression.
    [('if m1 m2 m3)
     (a-normalize-name m1 (lambda (normalized-m1)
                              (k
                               `(if ,normalized-m1
                                    ,(a-normalize m2)
                                    ,(a-normalize m3)))))]
    [('set! a b)
     (a-normalize-name b (lambda (bn) (k `(set! ,a ,bn))))]
    [('set-car! a b)
     (a-normalize-name b (lambda (bn) (k `(set-car! ,a ,bn))))]
    [('set-cdr! a b)
     (a-normalize-name b (lambda (bn) (k `(set-cdr! ,a ,bn))))]
    [('define a b)
     (a-normalize-name b (lambda (bn) (k `(define ,a ,bn))))]
    [(fn . m*)
     (if (primitive-op? fn)
         (a-normalize-name* m* (lambda (t*) (k `(,fn . ,t*)))))
         (a-normalize-name fn (lambda (t) (a-normalize-name* m* (lambda (t*) (k `(,t . ,t*))))))]
    [else
     (k m)]))

(define (a-normalize-name m k)
  ;;
  ;;  Values
  ;;     constant or variables.
  (define (value? v)
    (not (pair? v)))
  (a-normalize-with-k m
             (lambda (n) (if (value? n)
                             (k n)
                             (let1 t (gensym)
                               `(let1 ,t, n ,(k t)))))))

(define (a-normalize-name* m* k)
  (if (null? m*)
      (k '())
      (a-normalize-name (car m*)
                      (lambda (t)
                        (a-normalize-name* (cdr m*)
                                           (lambda  (t*) (k `(,t . ,t*))))))))

;;--------------------------------------------------------------------
;;
;; Tests
;;

;; one body
(with-gensym
 (a-normalize '(let1 a 3 (+ (+ 1 2)))))
(eqt '(let1 a 3 (let1 G4 (+ 1 2) (+ G4))) (with-gensym (a-normalize '(let1 a 3 (+ (+ 1 2))))))

;; two bodies
(with-gensym
 (a-normalize '(let1 a 3 (+ (+ 1 2)) (+ (+ 3 3)))))
(eqt '(let1 a 3 (let1 G4 (+ 1 2) (+ G4)) (let1 G8 (+ 3 3) (+ G8))) (with-gensym (a-normalize '(let1 a 3 (+ (+ 1 2)) (+ (+ 3 3))))))

;; one body
(with-gensym
 (a-normalize '(lambda () (+ (+ 1 2)))))
(eqt '(lambda () (let1 G4 (+ 1 2) (+ G4))) (with-gensym (a-normalize '(lambda () (+ (+ 1 2))))))

;; two body
(with-gensym
 (a-normalize '(lambda () (+ (+ 1 2)) (+ (+ 3 4)))))
(eqt '(lambda () (let1 G4 (+ 1 2) (+ G4)) (let1 G8 (+ 3 4) (+ G8))) (with-gensym (a-normalize '(lambda () (+ (+ 1 2)) (+ (+ 3 4))))))

;; set!
(with-gensym
 (a-normalize '(set! a (+ (+ 1 2)))))
(eqt '(let1 G7 (+ 1 2) (let1 G8 (+ G7) (set! a G8))) (with-gensym (a-normalize '(set! a (+ (+ 1 2))))))

;; let1
(a-normalize '(if (let1 x 3 x) 4 5))
(eqt '(let1 x 3 (if x 4 5)) (a-normalize '(if (let1 x 3 x) 4 5)))

;; let1
(with-gensym
 (a-normalize '(+ (+ 2 2) (let1 x 1 (f x)))))
(eqt '(let1 G7 (+ 2 2) (let1 x 1 (let1 G8 (f x) (+ G7 G8)))) (with-gensym (a-normalize '(+ (+ 2 2) (let1 x 1 (f x))))))

;; let1
(with-gensym
 (a-normalize '(+ (let1 a 0 (+ a 3)) 2)))
(eqt '(let1 a 0 (let1 G4 (+ a 3) (+ G4 2))) (with-gensym (a-normalize '(+ (let1 a 0 (+ a 3)) 2))))

;; let1
(with-gensym
 (a-normalize '(let1 a (let1 b 3 (+ b 2))
                 (+ a 2))))
(eqt '(let1 b 3 (let1 a (+ b 2) (+ a 2))) (with-gensym (a-normalize '(let1 a (let1 b 3 (+ b 2)) (+ a 2)))))

;; lambda
(with-gensym
 (a-normalize '(lambda () (+ (+ 1 2) 3))))
(eqt '(lambda () (let1 G4 (+ 1 2) (+ G4 3))) (with-gensym (a-normalize '(lambda () (+ (+ 1 2) 3)))))

(a-normalize '(if (+ 2) 3 4))
(eqt '(let1 G1117 (+ 2) (if G1117 3 4)) (a-normalize '(if (+ 2) 3 4)))

(test-end)