ese-eval (was 写経)

処理系の基礎を学ぼうぜ的な ese-eval 講座 by Mさん。
ノートにスタックを書きながら SchemeGauche)で書いてる。

  • 一般化らしい
  • 引数の扱いが make-flatten-cont あたり。
  • add は可変引数に対応している。


短いけど頭が混乱するな。
だいぶ難しくなってきましたよ。> Mさん


個人的には↓の部分で cut と fold が自然に頭に浮かんだのが成長を感じさせる(そこかよ

((add? exp1)
  (queue-push! stack (make-func (make-flatten-cont (assq-ref exp1 'args) (cut fold + 0 <>)))))

全コード

(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-add  . args)
  `((type . add)
    (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 (cont? exp)
  (type-eq? exp 'cont))

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

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

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

(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 (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))))
       ((add? exp1)
        (queue-push! stack (make-func (make-flatten-cont (assq-ref exp1 'args) (cut fold + 0 <>)))))
       ((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))
                (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-end)