小さな Forth もどきを作ってみた

(def-primitive :|create| のことろで codes-index を 0 に設定してなかったので、2回目以降の呼び出しが何もせず終っていた。
修正した。(2005/05/05)


今日はちょっと初心にかえって小さな Forth もどきを作ってみた。
実装は Common Lisp で、参考にしたのはもちろん jonesforth.

: 1+ 1 + ;
9 1+ .

で10を出力くらしか動かないけど。

それでもこんな小さなコードでまがりなりにもプログラムが動くってのは不思議に思える。

(defparameter *data-stack* nil)
(defparameter *return-stack* nil)
(defparameter *code-point* nil)
(defparameter *dictionary* (make-hash-table))

(defstruct codes
  (codes (make-array 0 :adjustable t :fill-pointer t))
  (index 0))

(defun codes-next (&optional (codes *code-point*))
  (unless (codes-end-p codes)
    (prog1
        (aref (codes-codes codes) (codes-index codes))
      (incf (codes-index codes)))))

(defun codes-end-p (&optional (codes *code-point*))
  (>= (codes-index codes) (length (codes-codes codes))))

(defun run ()
  (loop
     while
     (progn
       (loop while (and *code-point* (codes-end-p))
          do (setf *code-point* (por)))
       ;;(format t "~%~s~%~s~%~s~%" *return-stack* *data-stack* *code-point*)
       *code-point*)
     do (let ((word (codes-next)))
          (let ((def (fnd word)))
            (if def
                (funcall def)
                (error "~s is unbound." word))))))

(defun put-dic (k v)
  (setf (gethash k *dictionary*) v))

(defun get-dic (k)
  (gethash k *dictionary*))

(defmacro def-primitive (word (&optional immediate) &body body)
  `(progn
     (setf (get ',word 'immediate) ,immediate)
     (put-dic ,word #'(lambda () ,@body))))

(defmacro def-word (word (&optional immediate) &body body)
  `(progn
     (setf (get ',word 'immediate) ,immediate)
     (put-dic ,word #'(lambda ()
                        (pur *code-point*)
                        (setf *code-point*
                              (make-codes :codes (coerce ',body 'vector)))))))

(defun fnd (word)
  (get-dic word))

(defun po ()
  (pop *data-stack*))

(defun pu (x)
  (push x *data-stack*))

(defun por ()
  (pop *return-stack*))

(defun pur (x)
  (push x *return-stack*))


(def-primitive :|t| ()
               (pu t))
(def-primitive :|nil| ()
               (pu nil))
(defgeneric g= (x y)
  (:method (x y)
    (equal x y)))
(def-primitive :|=| ()
               (pu (g= (po) (po))))

(def-primitive :|dup| ()
               (pu (car *data-stack*)))

(def-primitive :|+| ()
               (pu (+ (po) (po))))

(def-primitive :|.| ()
               (print (po)))

(def-primitive :|lit| ()
               (pu (codes-next)))
(def-primitive :|exit| ()
               (setf *code-point* (por)))
(def-primitive :|create| ()
               (let ((word (po)))
                 (let ((codes (make-codes)))
                   (put-dic word #'(lambda ()
                                     (pur *code-point*)
                                     (setf (codes-index codes) 0)
                                     (setf *code-point* codes)))
                   (def-primitive :|latest| ()
                                  codes))))
(defun latest ()
  (funcall (fnd :|latest|)))
(defun comma (word)
  (let ((codes (latest)))
    (vector-push-extend word (codes-codes codes))))
(def-primitive :|,| ()
               (comma (po)))
(def-primitive :|immediate| (t)
               (let ((latest (latest)))
                 (maphash #'(lambda (k v)
                              (when (eq v latest)
                                (setf (get k 'immediate) t)))
                          *dictionary*)))
(def-primitive :|branch| ()
               (let ((offset (1- (codes-next)))) ; 1- is offset it self.
                 (incf (codes-index *code-point*) offset)))
(def-primitive :|0branch| ()
               (if (po)
                   (incf (codes-index *code-point*))
                   (incf (codes-index *code-point*)
                     (codes-next))))

(def-primitive :|cons| ()
               (let ((y (po))
                     (x (po)))
                 (pu (cons x y))))
(def-primitive :|car| ()
               (pu (car (po))))
(def-primitive :|cdr| ()
               (pu (cdr (po))))
(def-primitive :|cons?| ()
               (pu (consp (po))))

(def-primitive :|{}| ()
               (pu (make-hash-table)))

(def-primitive :|*stdin*| ()
               (pu *standard-input*))
(def-primitive :|read-char| ()
               (pu (read-char (po) nil nil)))
(def-primitive :|*stdout*| ()
               (pu *standard-output*))
(def-primitive :|write-char| ()
               (let ((stream (po))
                     (char (po)))
                 (write-char char stream)))

(defun word ()
  (let ((scaned (with-output-to-string (out)
                  (loop for c = (read-char *standard-input* nil nil)
                     if (char> c #\space)
                     do (return (write-char c out)))
                  (loop for c = (read-char *standard-input* nil nil)
                     do (if (char> c #\space)
                            (write-char c out)
                            (return))))))
    (intern scaned :keyword)))
(def-primitive :|word| ()
               (pu (word)))

(def-primitive :|state| ()
               (pu nil))
(def-primitive :|[| (t)
               (def-primitive :|state| ()
                              (pu nil))) ; immediate mode
(def-primitive :|]| ()
               (def-primitive :|state| ()
                              (pu t)))  ; compile mode

(def-word :|quit| ()
          :|interpret|
          :|branch|
          -2)

(def-primitive
    :|interpret| ()
    (let ((word  (progn
                   ;;(format t "~%~s~%~s~%~s~%" *return-stack* *data-stack* *code-point*)
                   (word))))
      (when (eq word :|bye|)
        (break))
      (let ((fnd (fnd word)))
        (if fnd
            (if (get word 'immediate)
                (funcall fnd)
                (if (progn (funcall (fnd :|state|)) (po))
                    (comma word)
                    (funcall fnd)))
            (let ((literal (read-from-string (symbol-name word))))
              (when (symbolp literal)
                (error "~s is unknown." word))
              (if (progn (funcall (fnd :|state|)) (po))
                  (progn
                    (comma :|lit|)
                    (comma literal))
                  (pu literal)))))))


(def-word :|:| ()
          :|word| :|create| :|]|)
(def-word :|;| (t)
          :|[|)


;;;; 以下テスト
(def-word :|double| ()
          :|+| :|+|)

(progn
  (setf *code-point*
        (make-codes :codes #(:|lit| 1 :|lit| 2 :|lit| 3 :|double|)))
  (run)
  (assert (= 6 (print (po)))))

(progn
  (setf *code-point*
        (make-codes
         :codes
         #(:|lit| 1 :|lit| 2 :|lit| 3 :|lit| 4 :|lit| 5
           :|lit| :|triple| :|create|
           :|lit| :|+| :|,|
           :|lit| :|+| :|,|
           :|lit| :|+| :|,|
           :|lit| :|+.| :|create|
           :|lit| :|+| :|,|
           :|lit| :|dup| :|,|
           :|lit| :|.| :|,|
           :|triple|
           :|+.|)))
  (run)
  (assert (= 15 (print (po)))))

(progn
  (setf *code-point* (make-codes :codes #(:|quit|)))
  (run))