'(qlot Caveman2 Clack Roswell jscl)この辺りの組み合わせでようやく雛形のようなものができた。

やろうとしたこと

jsclというCommon LispをJavaScriptにコンパイルしてくれるものを使って、フロントエンドもCommon Lispで書こうとした。

github.com

見よう見まねで作ったので、そこはこうするのが良い。といったことがあればコメントなりTwitterでなり指摘していただけると喜びます。

Lisp Advent Calendar 2016の記事です。

Node.jsに慣れすぎて、Common Lispだと何かにつけてはまる。はまるたびに正解がわからない。

jsclを使ってコンパイルするところで力尽きたので、jsclでフロントエンドというところまでできていない。

成果物

github.com

やっていることは /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 ) ( let (( js ( format nil "~alisp/~a.js" *static-directory* lisp )) ( lisp ( format nil "~alisp/~a.lisp" *static-directory* lisp ))) ( 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 )) ( 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.lispをロードしてくれるわけではなかったので，明示的に しているところをもっと正しくしたい jscl側のasdファイルの書き方がわからない． (ql:quickload :jscl) した時にloadしたり何か実行する手段があるのか不明

した時にloadしたり何か実行する手段があるのか不明 qlot install ではなく ros install peccu/jscl でも同様に(ql:quickload :jscl)できる？

今後の展望

jscl側をいじり始めるとコンポーネント化したくなってくるので、jsclに乗っかるフレームワークを作りたくなってくる。

jsclによるフロントエンドはまた別の記事にする。