defclass-star 便利

defclass は defstruct にくらべると、:initarg や :initform を明示的に指定しなければならないあたりが面倒。
それを解決してくれのが defclass-star.

;; こんなふうに書くと
(defclass* cat ()
  ((eye-color "blue")
   (body-color "シールポイント")
   (lovely t :type boolean)))

;; こんなふうに展開される。
(DEFCLASS CAT NIL
  ((EYE-COLOR :INITFORM "blue" :ACCESSOR EYE-COLOR-OF :INITARG
              :EYE-COLOR)
   (BODY-COLOR :INITFORM "シールポイント" :ACCESSOR BODY-COLOR-OF :INITARG
               :BODY-COLOR)
   (LOVELY :INITFORM T :ACCESSOR LOVELY-P :INITARG :LOVELY :TYPE
           BOOLEAN)))
type boolean とするとちゃんと
accessor が xxx-p となってくれる。

この人は :accessor を xxx-of にするんだね。
このへんの命名規則は歴史的にもいろいろとあるのかな?

小さな 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))

Forth で無名関数の再帰

Perl で JS の arguments.callee 的なことしようと思ってハマった - IT戦記 の件を Forth で。

Forth なら recurse がある。

:noname dup if dup . 1 - recurse else . then ;  ok
10 swap  ok
.s <2> 10 47312420583688  ok
execute  10 9 8 7 6 5 4 3 2 1 0  ok

ところが、名前付きの関数で普通に再帰しようとすると

: foo ." hello" foo ; 
*the terminal*:1: Undefined word
: foo ." hello" foo ;
                ^^^

とエラーになる。

さらに、既に定義してあれば古い定義が呼ばれる。

: bar ." World!" ;  ok
: bar cr  ." Hello " bar ; redefined bar   ok
bar
Hello World! ok

Common Lisp で無名関数の再帰

Common Lisp だとこんな感じ?

(defmacro rlambda (lambda-list &body body)
  `(labels ((self ,lambda-list
              ,@body))
     #'self))

(funcall
 (rlambda (n)
   (print n)
   (unless (zerop n) (self (1- n))))
 10)

ん? むしろ引数に呼び出し元を持たせるのかな?

funcall read-from-string するわけ

Slime の起動では次のように read-from-string して funcall している。

(progn (load "/slime/swank-loader.lisp" :verbose t) (funcall (read-from-string "swank-loader:init")) (funcall (read-from-string "swank:start-server") "/tmp/slime.13550" :coding-system "utf-8-unix"))

以前なんでだろう? と疑問に思ったけどスルーしていたが、今日理由がわかった。
read した時点ではまだ swank パッケージが存在しないからだ。
Common Lisp では存在しないパッケージのシンボルを read しようとするとエラーとなる。
(load "/slime/swank-loader.lisp" :verbose t) が終わるまでは普通に swank パッケージのシンボルを書くことができない。
その対策として read 時には文字列で、実行時にシンボルにする。
なるほど。

Drakma 使用時の注意事項

Drakma - A Common Lisp HTTP client を使うときの注意事項。

  • *drakma-default-external-format* で external-format を指定しておく。
  • text/* 以外の Content-Type をテキストとして扱いたいときは、*text-content-types* に追加する。
  • Content-Length は省略すると文字数でカウントされるため、バイト数で明示的に指定する。
(use-package :drakma)

;; UTF-8
(setq *drakma-default-external-format* :utf-8)

;; application/atom+xml をバイナリではなくテキストとして扱う。
(pushnew (cons "application" "atom+xml") *text-content-types*
         :test #'equal)

;; Content-Length はバイトサイズで指定する。
(http-request "http://www.example.com/"
              :method :post
              :content post-data
              :content-length (length (sb-ext:string-to-octets
                                       post-data :external-format :utf-8)))