'(qlot Caveman2 Clack Roswell jscl)この辺りの組み合わせでようやく雛形のようなものができた。
やろうとしたこと
jsclというCommon LispをJavaScriptにコンパイルしてくれるものを使って、フロントエンドもCommon Lispで書こうとした。
見よう見まねで作ったので、そこはこうするのが良い。といったことがあればコメントなりTwitterでなり指摘していただけると喜びます。
Lisp Advent Calendar 2016の記事です。
Node.jsに慣れすぎて、Common Lispだと何かにつけてはまる。はまるたびに正解がわからない。
jsclを使ってコンパイルするところで力尽きたので、jsclでフロントエンドというところまでできていない。
成果物
やっていることは/lisp/:lisp.js
というパスへのアクセスに対し、:lisp.js
があればそれを返し、なければ同名の:lisp.lisp
ファイルをコンパイルして返すだけ。
起動させるには
Roswellをインストール済みとして、下記一連のコマンドで起動させられる。 Intel Edisonで動作確認したところsbcl/1.3.11 だとironcladがエラー吐いたので1.3.9を使った。
$ ros install sbcl/1.3.9 $ ros install peccu/caveman-jscl $ ros install qlot $ cd ~/.roswell/local-projects/peccu/caveman-jscl $ qlot install $ ros install clack $ qlot exec clackup app.lisp ... Hunchentoot server is going to start. Listening on localhost:5000.
これで http://localhost:5000/ にアクセスすると/lisp/log.js
を読み込んでコンソール出力しているのが見える.
static/lisp/
に*.lisp
をおけば/lisp/*.js
でアクセスできるようになる。
一応ファイルの更新日時を見てlispの方が新しければ再コンパイルするようにしている。
サンプルではlog.lisp という下記のようなファイルを設置している.
(#j:console:log "") (#j:console:log "--------lisp was compiled!!--------")
コンパイルされたjs
(function(jscl) { 'use strict'; (function(values, internals) { var l1 = internals.make_lisp_string(''); var l2 = internals.intern('*ROOT*'); var l3 = internals.make_lisp_string('console'); var l4 = internals.make_lisp_string('log'); internals.js_to_lisp(internals.symbolValue(l2)[internals.xstring(l3)][internals.xstring(l4)](internals.lisp_to_js(l1))); var l5 = internals.make_lisp_string('--------lisp was compiled!!--------'); var l6 = internals.make_lisp_string('console'); var l7 = internals.make_lisp_string('log'); internals.js_to_lisp(internals.symbolValue(l2)[internals.xstring(l6)][internals.xstring(l7)](internals.lisp_to_js(l5))); })(jscl.internals.pv, jscl.internals); })(typeof require !== 'undefined' ? require('./jscl') : window.jscl)
lispをコンパイルする部分
src/web.lisp
caveman-jscl/web.lisp at 1bcf6684094f95520ef02f49ed2655411bdd7414 · peccu/caveman-jscl · GitHub
(defroute "/lisp/:lisp.js" (&key lisp) ;; jsファイルとlispファイルのパスを作る (let ((js (format nil "~alisp/~a.js" *static-directory* lisp)) (lisp (format nil "~alisp/~a.lisp" *static-directory* lisp))) ;; jsファイルが存在しないか、jsファイルの方が古ければコンパイルする (when (or (and (not (probe-file js)) (probe-file lisp)) (< (file-write-date js) (file-write-date lisp))) (in-package :jscl) (jscl::compile-application `(,lisp) js) (in-package :caveman-jscl.web)) ;; コンパイル結果、jsファイルが作成されていれば返し,そうでなければ404を返す (if (probe-file js) (render js) (throw-code 404))))
わからないところ
主にjsclを読み込むところで困った。 jsclがQuicklispに登録されていればよかったのだが未登録なので、Forkしたリポジトリでasdファイルを作ってqlotでインストールする形になっている。
defsystem
の:depends-on
とdefpackage
の:use
の違いが未だにわからない。- depends-onに書くとQuicklispが一緒にインストールとロードしてくれる?
- useに書くとロード済みなら、名前空間が利用できるようになる?
(ql:quick load :jscl)
ではjscl.lispをロードしてくれるわけではなかったので,明示的に(load (merge-pathnames "jscl.lisp" (asdf:system-source-directory :jscl)))
しているところをもっと正しくしたい- jscl側のasdファイルの書き方がわからない.
(ql:quickload :jscl)
した時にloadしたり何か実行する手段があるのか不明 qlot install
ではなくros install peccu/jscl
でも同様に(ql:quickload :jscl)できる?
今後の展望
jscl側をいじり始めるとコンポーネント化したくなってくるので、jsclに乗っかるフレームワークを作りたくなってくる。
jsclによるフロントエンドはまた別の記事にする。