脳汁でコードを書く

Archive for the ‘common lisp’ Category

Common Lispで中置記法

水曜日, 3 月 10th, 2010

lispでは前置記法が用いられますが, 数学的なプログラムの実装はなかなか難しいという問題があります.
Common Lispにはリードマクロがあるので, 中置記法を実現することができます.

本記事の内容はnurarihyonというパッケージで提供しているものです.

というわけで実装へ

今回は#%(…)というリードマクロを考えます. ここで…の部分に中置記法が可能なようにします.

ここで以下のような仕様にします

  1. <-, =で代入可能なようにする
  2. @で配列の要素にアクセスする
  3. 複数の引数をとる場合は$でその区切りを指定する

では以下実装.

(defun read-infix-sexp (stream n char)
  (declare (ignore n char))
  (let ((sexp (read stream)))
    (infix->prefix sexp)))
 
(defun infix->prefix/split$ (arg &optional (buf nil) (result nil))
  "this function separete arg by the symbol `$'"
  ;; first, just separate arg by $.
  ;; (1 $ 2 $ 3) -> ((1) (2) (3))
  ;; (1 + 2 $ 3) -> ((1 + 2) (3))
  ;; (1 + 2 $ 3) -> (+ 2 $ 3) (1)           ()
  ;;             -> (2 $ 3)   (1 +)         ()
  ;;             -> ($ 3)     (1 + 2)       ()
  ;;             -> (3)       ()            ((1 + 2))
  ;;             -> ()        (3)           ((1 + 2))
  ;;             ->                         ((1 + 2) (3))
  (cond ((null arg) (append result (list buf)))
        ((and (symbolp (car arg))
              (eq (chimi:symbol->keyword (car arg)) :$))
         (infix->prefix/split$ (cdr arg) nil (append result (list buf))))
        (t
         (infix->prefix/split$ (cdr arg) (append buf (list (car arg)))
                               result))))
 
(defun infix->prefix/function-call (a b c)
  "For example,
a := sin
b := (1)
c := another s-expression..."
  ;; its deficult to estimate the number of arguments of b.
  ;; so we utilize another syntax `$' for separate arguments.
  (let ((function-sexp
         (cons a (mapcar #'infix->prefix (infix->prefix/split$ b)))))
    (if c
        ;; if there is c, we need to resolve c to operator and its args.
        (destructuring-bind (operator & args) c
          (list operator function-sexp (infix->prefix args)))
        function-sexp)))
 
(defun %infix->prefix (sexp)
  (destructuring-bind (a &optional b &rest c) sexp ;(a b . c)
    (cond ((and (not (null b)) (listp b))  ;when b is list
           ;; here, we check sexp like (sin(x) ...)
           (infix->prefix/function-call a b c))
          ((and a b c)
           (let ((bsym (chimi:symbol->keyword b)))
             (case bsym
               (:@
                ;; @ works as aref a @ (1 2) -> (aref a 1 2)
                ;; a @ 1 -> (aref a 1)
                ;; here we need to think aboud (cdr c) too...
                (destructuring-bind (index &rest args) c
                  (let ((this-section
                         (if (listp index)
                             (append (list 'aref a) index)
                             (append (list 'aref a) (list index)))))
                    (if args
                        (destructuring-bind (operator &rest op-args) args
                          (list (infix->prefix operator) ;no need?
                                this-section
                                (infix->prefix op-args)))
                        this-section))))
               (t
                (list (infix->prefix b)
                      (infix->prefix a) (infix->prefix c))))))
          ((and b (null c)) ; no c, it means function appling like sin(x)
           (list (infix->prefix a) (infix->prefix b)))
          ((and (null b) (null c)) (infix->prefix a))))) ;only a
 
(defun infix->prefix (sexp)
  "This function converts an infix s-expression to a prefix s-expression."
  (cond
    ((and (symbolp sexp)
          (or (eq (chimi:symbol->keyword sexp) :<-)
              (eq (chimi:symbol->keyword sexp) :=)))
     'setf)                              ;setf alias
    ((listp sexp) (%infix->prefix sexp)) ;we need to convert
    (t sexp)))                           ;may be literal

ここで, chimi:keyword->symbolは以下のような実装です

(defun symbol->keyword (sym)
  "convert a symbol to keyword.
  ;;; (symbol->keyword 'hoge) -> :hoge"
  (declare (type symbol sym))
  (intern (string sym) :keyword))

これを使うと中置記法ができます.

(set-dispatch-macro-character #\# #\% 'read-infix-sexp)
(defvar a 0)
#%(a <- 1 + 2 + atan(1.0 $ 1.0)) ;; expand to (SETF A (+ 1 (+ 2 (ATAN 1.0 1.0)))) a = 3.7853982
(defvar b (make-array 3 :initial-element 3))
#%(b @ (0) + 100.0) ;; => 103.0 expanded as (+ (AREF B 0) 100.0)

以外とみにくいですねw

snow leopardでnurikabe

月曜日, 3 月 8th, 2010

このあいだのエントリーのpatchによってsnow leopard x sbclでnurikabeが動くようになりました.

nurikabeは私が作っているGUIライブラリです.

ソースコードはここから入手できます.

ちなみに, これのソースは以下のような感じです.

(require :nurikabe)
 
(defvar *manager* (nk:init-gui :loggingp t :threadingp t))
(defvar *win* (nk:make-window :width 300 :height 200
                              :background :white
                              :name "test window"))
(defvar *widget1* (nk:make-widget 'nk:<image-widget>
                                 :x 0 :y 0
                                 :width 150 :height 100
                                 :parent *win*
                                 :background :blue))
(defvar *widget2* (nk:make-widget 'nk:<image-widget>
                                 :x 150 :y 0
                                 :width 150 :height 100
                                 :parent *win*
                                 :background :green))
(defvar *widget3* (nk:make-widget 'nk:<image-widget>
                                  :x 0 :y 100
                                  :width 150 :height 100
                                  :parent *win*
                                  :background :red))
(defvar *widget4* (nk:make-widget 'nk:<image-widget>
                                  :x 150 :y 100
                                  :width 150 :height 100
                                  :parent *win*
                                  :background :white))
 
(nk:draw-line (nk:image-of *widget1*) 0 0 100 100)
(nk:draw-circle (nk:image-of *widget2*) 75 50 30)
(nk:draw-string (nk:image-of *widget3*) "NURIKABE" 0 0 :font-size 30)
(nk:draw-rectangle (nk:image-of *widget4*) 0 0 100 100)
 
(nk:render-widgets *win*)

snow leopard(x86-64)のsbclで普通のdlopenを利用する

日曜日, 3 月 7th, 2010

snow leopard上のsbclは大体うまく動くのですが, FFIでCの関数を呼びだしはじめると, 結構うまくいかないことがあります.

snow leopard上でsbclを使ってるひとは

(load-shared-object "/usr/X11R6/lib/libX11.dylib")
(load-shared-object "/usr/X11R6/lib/libGL.dylib")

とすると, プロセスが帰ってこなくなることが確認できます.

これは, darwinにおいて, sbclは普通のdlopenを利用してないことが原因だと思われます.

これはdarwinは標準的なELFではなく, バイナリがMach-o形式のため, dlopenを<mach-o/dyld.h>を利用してエミュレートしているのが原因だと思われます(sbcl/src/runtime/darwin-dlshim.c).

しかし, よく考えると, mac os xのgccは普通にdlopenを提供してくれているので, そっちを使うほうが良いです.

その辺のことがPHPまわりで議論されてるようです. ruby 1.8.6だloadできないけど, 1.8.7だとできるみたいな話もあるらしいです (url紛失).

以下あやしいところもありますが, このためのパッチになります.

利用してるのはcvsの最新のものです(1.0.36.13).

Index: sb-bsd-sockets/defpackage.lisp
===================================================================
RCS file: /cvsroot/sbcl/sbcl/contrib/sb-bsd-sockets/defpackage.lisp,v
retrieving revision 1.14
diff -r1.14 defpackage.lisp
76a77,79
>
> #+darwin
> (load-shared-object "/usr/lib/libc.dylib")
Index: contrib/sb-posix/defpackage.lisp
===================================================================
RCS file: /cvsroot/sbcl/sbcl/contrib/sb-posix/defpackage.lisp,v
retrieving revision 1.16
diff -r1.16 defpackage.lisp
26a27,29
>
> #+darwin
> (sb-alien:load-shared-object "/usr/lib/libc.dylib")
Index: src/code/unix-foreign-load.lisp
===================================================================
RCS file: /cvsroot/sbcl/sbcl/src/code/unix-foreign-load.lisp,v
retrieving revision 1.1
diff -r1.1 unix-foreign-load.lisp
34a35
>
69c70,71
<   (let* ((extern (extern-alien-name symbol))
---
>   (let* ((extern #!-mach-o (extern-alien-name symbol)
>                  #!+mach-o (coerce symbol 'base-string))
Index: src/runtime/Config.x86-64-darwin
===================================================================
RCS file: /cvsroot/sbcl/sbcl/src/runtime/Config.x86-64-darwin,v
retrieving revision 1.4
diff -r1.4 Config.x86-64-darwin
13,14c13,15
< LINKFLAGS += -mmacosx-version-min=10.4
< OS_SRC = bsd-os.c x86-64-bsd-os.c darwin-os.c x86-64-darwin-os.c darwin-dlshim.c darwin-langinfo.c
---
> LINKFLAGS += -mmacosx-version-min=10.4 -ldl
> #OS_SRC = bsd-os.c x86-64-bsd-os.c darwin-os.c x86-64-darwin-os.c darwin-dlshim.c darwin-langinfo.c
> OS_SRC = bsd-os.c x86-64-bsd-os.c darwin-os.c  darwin-langinfo.c
Index: src/runtime/Config.x86-64-darwin9+
===================================================================
RCS file: /cvsroot/sbcl/sbcl/src/runtime/Config.x86-64-darwin9+,v
retrieving revision 1.1
diff -r1.1 Config.x86-64-darwin9+
14c14,15
< OS_SRC = bsd-os.c x86-64-bsd-os.c darwin-os.c x86-64-darwin-os.c darwin-dlshim.c darwin-langinfo.c
---
> #OS_SRC = bsd-os.c x86-64-bsd-os.c darwin-os.c x86-64-darwin-os.c darwin-dlshim.c darwin-langinfo.c
> OS_SRC = bsd-os.c x86-64-bsd-os.c darwin-os.c x86-64-darwin-os.c darwin-langinfo.c
Index: src/runtime/x86-64-darwin-os.c
===================================================================
RCS file: /cvsroot/sbcl/sbcl/src/runtime/x86-64-darwin-os.c,v
retrieving revision 1.14
diff -r1.14 x86-64-darwin-os.c
25a26,29
> // added by garaemon
> #include <dlfcn.h>
> #include <sys/wait.h>
> #include <mach-o/dyld.h>
688a693,697
> //dummy function
> /* void darwin_waitpid(int pid, int* status, int options) { */
> /*     waitpid(pid, status, options); */
> /* } */
>
Index: tools-for-build/ldso-stubs.lisp
===================================================================
RCS file: /cvsroot/sbcl/sbcl/tools-for-build/ldso-stubs.lisp,v
retrieving revision 1.24
diff -r1.24 ldso-stubs.lisp
327,331c327,337
<                  #!-darwin
<                  '("dlclose"
<                    "dlerror"
<                    "dlopen"
<                    "dlsym")
---
>                  ;; #!-darwin
>                   '("dlclose"
>                     "dlerror"
>                     "dlopen"
>                     "dlsym")
>                   #!+darwin
>                   '("waitpid"
>                     "ptsname"
>                     "grantpt"
>                     "unlockpt")
>                   ;; for bsd-sockets...?

sb-posixとsb-bsd-socketsでlibc.dylibをloadしなくてはいけなくなったのは理由がよくわかりません… -lcはつけてるはずですが

追記:

バグレポートしておいた

https://bugs.launchpad.net/sbcl/+bug/533470

追記2:

slimeでthreadを利用してる場合は, load-shared-objectでハングする可能性があります. dlopenはmmapとかしてそうだからそのへんかな?

以下解決のためのpatch

Index: swank-sbcl.lisp
===================================================================
RCS file: /project/slime/cvsroot/slime/swank-sbcl.lisp,v
retrieving revision 1.269
diff -r1.269 swank-sbcl.lisp
71c71
<     ((member :sb-thread *features*) :spawn)
---
>     ;;((member :sb-thread *features*) :spawn)
1345c1345
< #+(and sb-thread
---
> #+(and nil sb-thread

CLで幾何モデルライブラリを作ろう(5)

日曜日, 10 月 18th, 2009

githubにてコードを追加しました

https://github.com/garaemon

仕様変更, というかこれからガシガシ変更していきます.

導入方法や動作環境はまた今度.

ドキュメンテーションもそのうち作ります.

あと, ちょっと攻殻機動隊っぽいデモをつくりました. REPLとの連携もみれます.

komainu sample

追記

動作環境について.

SBCLでかつマルチスレッドがつかえる環境でしか動きません.

というわけでlinuxでしかうごきません.

macはsnow leopardではOpenGLのdylibのffiがうまくいきません.

環境構築はclbuildを使うと楽.
以下は動作確認してないので注意

my-projectsに以下を追加

lisp-unit get_git git://repo.or.cz/lisp-unit.git
cl-interpol get_ediware
 
chimi get_git git://github.com/garaemon/chimi.git
nurarihyon get_git git://github.com/garaemon/nurarihyon.git
nurikabe get_git git://github.com/garaemon/nurikabe.git
komainu get_git git://github.com/garaemon/komainu.git
yasha get_git git://github.com/garaemon/yasha.git
tengu get_git git://github.com/garaemon/tengu.git
clyax get_git git://github.com/garaemon/clyax.git

さらにdependencyに以下を追加

cl-interpol cl-unicode
chimi iterate cl-interpol log5 cl-ppcre alexandria cffi
nurarihyon chimi
nurikabe chimi cl-vectors iterate cffi clyax nurarihyon alexandria
komainu chimi nurarihyon nurikabe iterate
yasha chimi usocket
tengu chimi iterate
clyax cffi chimi

CLで幾何モデルライブラリを作ろう(4)

金曜日, 10 月 9th, 2009

透明なobjectをサポート.

transparent object

transparent object

これに必要なコードは以下

(require :komainu)
(use-package :komainu)
(use-package :nurarihyon)
(use-package :nurikabe)
(defvar *viewer* (make-komainu-viewer :loggingp nil))
(defvar *a* (make-cube 100.0 100.0 100.0 :color :red))
(defvar *b* (make-cube 200.0 200.0 200.0 :color :green))
(transparent *b* 0.5)
(objects *viewer* (list *A* *B* *world-coords*))

CLで幾何モデルライブラリを作ろう(3)

金曜日, 10 月 9th, 2009

テクスチャに対応.こんな感じ.


textured cube

textured cube

(require :komainu)
(use-package :komainu)
(use-package :nurarihyon)
(use-package :nurikabe)
 
(defvar *viewer* (make-komainu-viewer :loggingp t))
(defvar *a* (make-cube 100.0 100.0 100.0 :color :red))
(defvar *im* (make-image :width 300 :height 300 :background :green))
(draw-string *im* "Hoge" 150 150
             :font-size 0.03
             :color :black)
(paste-texture (car (faces-of *a*)) *im*
               (list (float-vector 0 1)
                     (float-vector 0 0)
                     (float-vector 1 0)
                     (float-vector 1 1)))
(objects *viewer* (list *a*))

このために, clxのGLX, OpenGLのバインディングを捨てて, すべてcffiでバインディングを1から作り直すことにした.

ちなみに, 2Dの描画はcl-vectorsをつかってベクター描画.

snow leopardでsbclをbuildしてみる

日曜日, 10 月 4th, 2009

snow leopardでsbclのbuildにてこずったのでメモ

まずいまのstable版ではmakeが通らない.
以下のようなエラーが出る.

//entering make-target-1.sh
//building runtime system and symbol table file
rm -f *.[do] sbcl sbcl.nm sbcl.h core *.tmp
echo '#include "genesis/config.h"' >sbcl.h
echo '#include "genesis/constants.h"' >>sbcl.h
gcc -g -Wall -O2 -fdollars-in-identifiers -mmacosx-version-min=10.4 -I. -no-cpp-precomp  -c -o alloc.o alloc.c
In file included from alloc.c:21:
runtime.h: In function 'CONS':
runtime.h:151: warning: cast to pointer from integer of different size
runtime.h: In function 'SYMBOL':
runtime.h:157: warning: cast to pointer from integer of different size
runtime.h: In function 'FDEFN':
runtime.h:163: warning: cast to pointer from integer of different size
/var/folders/3K/3K4LXRIJHoq8UNahYP7P4U+++TI/-Tmp-//cck88adh.s:81:Incorrect register `%rbx' used with `l' suffix
/var/folders/3K/3K4LXRIJHoq8UNahYP7P4U+++TI/-Tmp-//cck88adh.s:578:Incorrect register `%rdx' used with `l' suffix
make: *** [alloc.o] Error 1

x86, x86_64にするとか1.0.30のバイナリをつかうとかいろいろ試してみたけど, 最終的にcvsの先端だとバグがfixされている模様

$ cvs -d :pserver:anonymous@sbcl.cvs.sourceforge.net:/cvsroot/sbcl co sbcl

そしたら, multi threadつかいたいのでsbcl/customize-target-features.lispというファイルを作る

(lambda (features)
      (flet ((enable (x)
               (pushnew x features))
             (disable (x)
               (setf features (remove x features))))
        ;; Threading support, available only on x86/x86-64 Linux, x86 Solaris
        ;; and x86 Mac OS X (experimental).
        (enable :sb-thread)))

次にsbcl/sbclcomprというのを作る

SBCL_HOME=/usr/local/lib/sbcl /usr/local/bin/sbcl $*

そんでもって

$ sh make.sh "sbclcompr"
$ sudo sh install.sh

なんかrun-tests.shが大量のエラーをはいてるけど, みなかったことにしよう…

追記:

sh run-tests.shの結果

Finished running tests.
Status:
 Expected failure:    float.pure.lisp / (SCALE-FLOAT-OVERFLOW BUG-372)
 Expected failure:    float.pure.lisp / (ADDITION-OVERFLOW BUG-372)
 Expected failure:    threads.pure.lisp / WITHOUT-INTERRUPTS+CONDITION-WAIT
 Expected failure:    callback.impure.lisp / UNDERFLOW-DETECTION
 Invalid exit status: clos-add-remove-method.impure.lisp
 Invalid exit status: clos-cache.impure.lisp
 Invalid exit status: clos-interrupts.impure.lisp
 Invalid exit status: compare-and-swap.impure.lisp
 Invalid exit status: debug.impure.lisp
 Expected failure:    external-format.impure.lisp / (CHARACTER-DECODE-LARGE
                                                     FORCE-END-OF-FILE)
 Invalid exit status: gc.impure.lisp
 Invalid exit status: hash.impure.lisp
 Expected failure:    packages.impure.lisp / USE-PACKAGE-CONFLICT-SET
 Expected failure:    packages.impure.lisp / IMPORT-SINGLE-CONFLICT
 Invalid exit status: profile.impure.lisp
 Invalid exit status: threads.impure.lisp
 Invalid exit status: timer.impure.lisp
 Invalid exit status: core.test.sh
test failed, expected 104 return code, got 1

CLで幾何ライブラリを作ろう(2)

金曜日, 10 月 2nd, 2009

色とcylinderを追加してみた

こんなかんじ

komainu-viewer2

komainu-viewer2

 (require :komainu)
(use-package :komainu)
(use-package :nurarihyon)
(defvar *viewer* (make-komainu-viewer))
(defvar *a* (make-cube 100.0 100.0 100.0 :color :red))
(defvar *b* (make-cube 100.0 100.0 100.0 :color :green))
(translate (coords-of *a*) (float-vector 0 100 0))
(translate (coords-of *b*) (float-vector 0 -100 0))
(objects *viewer* (list *a* *b* *world-coords*
                        (make-cylinder 100 100)))

CLで幾何ライブラリを作ろう

木曜日, 10 月 1st, 2009

最近というかこの一瞬間くらいで猛烈に実装しているのですが, 幾何ライブラリとそのビューワーをせっせと作ってます.

これくらいのコードで

(require :komainu)
 
(defvar *viewer* (komainu:make-komainu-viewer))
 
(komainu:objects *viewer* (komainu:make-cube 100 100 100))

こんなかんじの表示が出ます.

komainu viewer

komainu viewer

ちなみにkomainuっていうパッケージが幾何ライブラリ.

依存するライブラリとしてはchimi(ユーティリティライブラリ), nurarihyon(数学ライブラリ), nurikabe(GUIライブラリ)があります.

もうちょっとできて, いろいろと落ち着いたら公開します.

描画系はclxを叩いてglxでGLの関数を呼んでいます. clxのバグがいくつか発見されたので, どうしようかなーと悩み中

描画系のモットーはインタプリタからの対話性を重視

CL::log5

日曜日, 9 月 6th, 2009

CLのloggerであるlog5についてまとめてみた.

これがloggerとしては標準なのかな?

http://garaemon.net/dokuwiki/doku.php?id=programming:lisp:commonlisp:log5