Common Lisp to JavaScriptコンパイラー「JSCL」の使い方
JSCLはCommon LispからJavaScriptへのコンパイラーです。サーバーサイドだけでなく、フロントもCommon Lispで書きたいけど、Parenscriptでは物足りないといった方におすすめです。
使い方
ソースをダウンロード
git clone https://github.com/jscl-project/jscl.git
ASDFは使わないので場所はどこでもいいです。
Common Lispの処理系を起動
お好きな処理系を起動。
jscl.lispをロード
(load "path/to/jscl/jscl.lisp")
JavaScript上でのCommn Lispのランタイムを作成
(jscl:bootstrap)
これによりjscl.jsが出来上がります。
自作コードのコンパイル
(jscl:compile-application files output)
filesに指定したファイルすべてをコンパイルして、outputで指定したファイルにその結果が出力されます。
※一度(jscl:bootstrap)
を実行しても、一旦Common Lispの処理系を終了した場合、再度(jscl:bootstrap)
を実行しないとコンパイルはできません。
アプリケーションへの組み込み
コンパイルされたjscl.jsと自作スクリプトファイルをHTMLのscriptタグに指定することでブラウザ上でCommon Lispのコードを実行することができます。
<!DOCTYPE html>
<html>
<head>
<script src="jscl.js"></script>
<script src="{自作スクリプト}"></script>
</head>
<body>
</body>
</html>
JS変数、プロパティへのアクセス
JS側の変数を参照
#j:{JS変数名}
;; 例
#j:window:outerWidth
プロパティを参照
(jscl::oget {JSオブジェクト} {プロパティ名})
;; 例
(setq win #j:window)
(jscl::oget win "outerWidth")
値の変更はsetf
で可能です。
関数呼び出し
(#j:{JS変数名} 引数1 ... 引数N)
((jscl::oget {JSオブジェクト} {プロパティ名}) 引数1 ... 引数N)
;; 例
(#j:Math:tan 0.8)
※一旦lispの変数に束縛するとfuncallが要ります。
JavaScriptからCommon Lispのコードを実行する方法
jscl.evaluateString(str)
サンプルプログラム1
(defpackage :jscl-example
(:use :cl))
(in-package :jscl-example)
(defun fact (n)
(if (= n 0)
1
(* n (fact (1- n)))))
(defun attr (el name)
(jscl::oget el name))
(defun (setf attr) (value el name)
(setf (jscl::oget el name) value))
(defun set-attrs (el attrs)
(loop for (k v) on attrs by #'cddr
do (setf (attr el k) v)))
(defun create-element (name &optional attrs)
(let ((el (#j:document:createElement name)))
(when attrs
(set-attrs el attrs))
el))
(defun add-event-listener (el type fn)
((jscl::oget el "addEventListener") type fn))
(defun init ()
(let ((in-el (create-element "input" '("type" "text" "value" "10")))
(out-el (create-element "input" '("type" "text")))
(button (create-element "button" '("innerText" "Calc"))))
(add-event-listener button "click" (lambda (e)
;; ↓ Not supported.
;; (declare (ignore e))
(setf (attr out-el "value")
(fact (parse-integer (attr in-el "value"))))))
(#j:document:body:append in-el button out-el)))
(add-event-listener #j:document "DOMContentLoaded" (lambda (e) (init)))
このファイルを「example.js」としてコンパイルします。(ディレクトリはご自由に)
(jscl:compile-application '("~/common-lisp/jscl/example.lisp") "~/common-lisp/jscl/example.js")
「example.js」をHTMLに組み込み
※「example.html」、「jscl.js」、「example.js」は同じディレクトリに配置
<!DOCTYPE html>
<html>
<head>
<script src="jscl.js"></script>
<script src="example.js"></script>
</head>
<body>
</body>
</html>
ブラウザで「example.html」を開くとこのようになります。
「Calc」ボタンをクリックした結果
サンプルプログラム2
自作のCommon Lisp製のゲームをJSCLに移植しました。
元のものとは違い、セーブはできません。
注意点
JSCLはCommon Lispのサブセットなので実装されていない機能もあります。以下、私が確認したものです。(2023/12/02現在)
- arefが多次元配列に対応していない
- equalpがない
- sin cos tanがない
- 分数リテラル(1/2など)が使えない
また、実行時にエラーが発生した場合、そのエラー原因を特定するのはなかなか難しいです。
Discussion