ese-eval2 - begin (was 写経)

id:higepon:20070804:1186242639
id:higepon:20070807:1186498468
の続き。
native stack 使う版はすんなり理解できるがこれは難しいね。
begin の導入。begin は引数(という言いかたはあまり良くないが)を順に評価していき、最後の評価結果を返す。
最後の引数の評価を tail というオブジェクトにしてスタックに積むのミソか。
んー。まだちょっと消化しきれていないな。
tail ではなくて、invoke-proc で全ての引数を同様に need-sub-exp にした場合との違いとかが分からない。
両方の場合のスタックの動きをみたりとか、特殊形式 if あたりを実装すると分かるかも


ここ1-2ヶ月で一番ワクワクする内容だと思うのだけど、反響がなくてMさんと笑うという内幕も。
(追記)Scheme的にその書き方はないだろなどツッコミ歓迎。

(use util.list)
(use gauche.test)
(use util.queue)

(define (make-int i)
  `((type . int)
    (value .  ,i)))

(define (make-need-sub-exp exp)
  `((type . need-sub-exp)
    (sub . ,exp)))

(define (make-tail exp)
  `((type . tail)
    (sub . ,exp)))

(define (make-add  . args)
  `((type . symbol)
    (name . add)
    (args . ,args)))

(define (make-begin  . args)
  `((type . symbol)
    (name . begin)
    (args . ,args)))

(define (make-func cont)
  `((type . func)
    (cont . ,cont)))

(define (type-eq? exp type)
  (eq? (assq-ref exp 'type) type))

(define (int? exp)
  (type-eq? exp 'int))

(define (add? exp)
  (eq? (assq-ref exp 'name) 'add))

(define (begin? exp)
  (eq? (assq-ref exp 'name) 'begin))

(define (ese-symbol? exp)
  (type-eq? exp 'symbol))

(define (func? exp)
  (type-eq? exp 'func))

(define (need-sub-exp? exp)
  (type-eq? exp 'need-sub-exp))

(define (tail? exp)
  (type-eq? exp 'tail))

(define (dump-queue queue)
  '())

(define (make-flatten-cont row-args proc)
  (let ((arg-index 0) (args (make-vector (length row-args))))
    (lambda (func)
      (define (invoke-proc)
        (if (< arg-index (length row-args))
            (make-need-sub-exp (list-ref row-args arg-index))
            (make-int (proc (vector->list args)))))
      (define (resume retval)
        (vector-set! args arg-index retval)
        (set! arg-index (+ 1 arg-index)))
      (cond ((eq? func 'invoke-proc)
             invoke-proc)
            ((eq? func 'resume)
             resume)))))

(define (make-begin-cont row-args)
  (let ((arg-index 0))
    (lambda (func)
      (define (invoke-proc)
        (if (< arg-index (- (length row-args) 1))
            (make-need-sub-exp (list-ref row-args arg-index))
            (make-tail (list-ref row-args arg-index))))
      (define (resume retval)
        (set! arg-index (+ 1 arg-index)))
      (cond ((eq? func 'invoke-proc)
             invoke-proc)
            ((eq? func 'resume)
             resume)))))

(define (ese-eval exp)
  (let ((stack (make-queue)) (ret '()))
    (queue-push! stack exp)
    (let loop ((dummy (dump-queue stack)) (exp1 (queue-pop! stack)))
      (cond
       ((int? exp1)
        (if (not (queue-empty? stack))
            (((assq-ref (queue-front stack) 'cont) 'resume) (assq-ref exp1 'value))
            (set! ret (assq-ref exp1 'value))))
       ((and (ese-symbol? exp1) (add? exp1))
        (queue-push! stack (make-func (make-flatten-cont (assq-ref exp1 'args) (cut fold + 0 <>)))))
       ((and (ese-symbol? exp1) (begin? exp1))
        (queue-push! stack (make-func (make-begin-cont (assq-ref exp1 'args)))))
       ((func? exp1)
        (queue-push! stack exp1)
        (let ((res (((assq-ref exp1 'cont) 'invoke-proc))))
          (cond ((need-sub-exp? res) (queue-push! stack (assq-ref res 'sub)))
                ((int? res)
                 (queue-pop! stack)
                 (queue-push! stack res))
                ((tail? res)
                 (queue-pop! stack)
                 (queue-push! stack (assq-ref res 'sub)))
                (else (error "never reached here"))))))
      (if (zero? (queue-length stack))
          ret
          (loop (dump-queue stack) (queue-pop! stack))))))


(test-start "ese-eval")
(test* "eval int" 10 (ese-eval (make-int 10)))
(test* "(add 10 12)" 22 (ese-eval (make-add (make-int 10) (make-int 12))))
(test* "(add 10 12 13)" 35 (ese-eval (make-add (make-int 10) (make-int 12) (make-int 13))))
(test* "(add (add 1 3) 10)" 14 (ese-eval (make-add  (make-add (make-int 1) (make-int 3)) (make-int 10))))
(test* "(add (add 1 3) 10 (add 4 5))" 23 (ese-eval (make-add  (make-add (make-int 1) (make-int 3)) (make-int 10) (make-add (make-int 4) (make-int 5)))))
(test* "(begin 10 12 13)" 13 (ese-eval (make-begin (make-int 10) (make-int 12) (make-int 13))))
(test* "(begin 10 12 (+ 1 3))" 4 (ese-eval (make-begin (make-int 10) (make-int 12) (make-add (make-int 1) (make-int 3)))))
(test-end)