新千葉 ガーベージ・コレクション

FPGA マガジンやインターフェースで書けなかったこと等をちょぼちょぼ書いてます。@ryos36

とりあえず hunchentoot ふたたび

ubuntu で試す

sudo apt install cl-hunchentoot でいけます。 インストールしただけだと何も使えません。悪いことに ubuntu のパッケージからは test が削られてます。まずは”素”の hunchentoot を動かします。

acceptor で動かす(ほとんど素)

#-:asdf (load "/usr/share/common-lisp/source/cl-asdf/asdf.lisp")
(require :hunchentoot)
(require :cl-who)
(setq hunchentoot:*hunchentoot-default-external-format*
     (flex:make-external-format :utf-8 :eol-style :lf))
(setq hunchentoot:*default-content-type* "text/html; charset=utf-8")

(hunchentoot:start
      (make-instance 'hunchentoot:acceptor :port 4242))

これで動きます。 localhost:4242 にアクセスして welcome のページが見れればまずは OK です。 f:id:ryos36:20190228152021p:plain

さて、これどこを見ているかというと、default の root というのが”コンパイル時"に決まるようになっていて、ubuntu で標準にいれれは /usr/share/common-lisp/source/hunchentoot/www です。 参考までに書くと make-instance 時に :document-root を指定すればそこが標準の root になります。

  (make-instance 'hunchentoot:acceptor :port 4242 :document-root "/home/WWW")

easy-acceptor で動かす

cgi 的なことが簡単にできます。easy と書いてありますが、あまり細かい設定をする必要がない場合、というかたいていの場合ではこちらを使うことになるかと思います。素の acceptor は本当に素で(機能的に分離したようだ)、dispatch-table 的なものすらありません(たぶん)。なので、acceptor を単純に使おうとすると、”恐らく"自分で handler を書き直さないといけなくて、その場合は acceptor を継承した class を作る必要がありそうです。 以前の hunchentoot は dispatch-table を自分で頑張って書いていた印象がありますが(うろおぼえ)、easy-acceptor を使えば handler を定義するだけで OK です。

#-:asdf (load "/usr/share/common-lisp/source/cl-asdf/asdf.lisp")
(require :hunchentoot)
(require :cl-who)
(setq hunchentoot:*hunchentoot-default-external-format*
     (flex:make-external-format :utf-8 :eol-style :lf))
(setq hunchentoot:*default-content-type* "text/html; charset=utf-8")

(hunchentoot:start
      (make-instance 'hunchentoot:easy-acceptor :port 4242))

(hunchentoot:define-easy-handler (say-yo :uri "/yo") (name)
  (setf (hunchentoot:content-type*) "text/plain")
  (format nil "Hey~@[ ~A~]!" name))

はい clisp でも sbcl でも ubuntu 上で動きました(追記: clisp 不安定。sbcl がおすすめです)。localhost:4242/yo にアクセスできれば OK です。localhost:4242/yo?name=yoyo などとラッパーの掛け合いよろしく遊んでください。 上のソースは実験的に start を先にしていますが、通常は逆でしょうね(start が後)。

古い情報に注意しましょう

この blog は 2019/2/28 に書いてますが、私はその情報で右往左往しました。 たぶん hunchentoot のバージョンが上がったことによる差異でしょう。今後は hunchentoot を使う時はパッケージ作って、バージョンチェックしないとダメかも。

まずは hunchentoot:start-server を使っているもの。相当古いので使えません。(私が知っていたのはこのあたり) 2008 年頃の blog に見ることが出来ます。 2011 年頃は、情報が途中ですね。acceptor だとたぶんほとんどと何もできません。サーバが立ち上がるので間違ってはないのですが。いくつかのサイトは acceptor をつかっています。easy-acceptor じゃないと動きませんでした。たぶん、いまや easy-acceptor でないと動かないと思います。

いろいろ調べる羽目に

動かすためにいろいろ調べる羽目になってしまいました。まずは asdf の設定を変えました。keen さん?とかを参考に ~/.config/common-lisp/source-registry.conf.d/ をつくり、10-systems.conf に (:tree "/usr/share/common-lisp/source/") と書いてます。そこで 00-debug.conf とかつくって (:tree (:home "Lisp/")) として、そのディレクトリに git から hunchentoot と usocket を持ってきました。usocket を持ってきた理由は、新しいバージョンの hunchentoot が新しいバージョンの usocket に依存していたためです。(めんどくさ~~い) これで最初は log-message* 関数を使ってログを出し(エラー出力に表示される)、最後はソース読んで改変して動きを確かめました。一苦労でした。

じみな macro

macrolet をつかってみる。だんだん世の中から外れていく気がする。

(defmacro make-style-lambda (style-desc)
  (macrolet ((make-style-lambda0 (style-desc)
              `#'(lambda ,
                   (remove-duplicates
                     (remove nil
                             (mapcar
                               #'(lambda (i)
                                   (if (listp i)
                                     (let ((qkey (car i))
                                           (arg-sym (cadr i)))
                                       (if (or (eq qkey 'SYSTEM::UNQUOTE)
                                               (eq qkey 'SYSTEM::SPLICE))
                                         arg-sym))))
                               (cadr style-desc))) :from-end t) ,style-desc)))
    (if (atom style-desc) (eval `(make-style-lambda0 ,(eval style-desc)))
      `(make-style-lambda0 ,style-desc))))

これで (make-style-lambda `(:a ,arg) とやっても

(let ((x `(:a ,arg))
   (make-style-lambda x))

としても思い通りの lambda が出来るようになった。これ最後 funcall するんだよね。funcall するマクロつくればいいね。

(defmacro do-w (x &body body) `(funcall (make-style-lambda ,x) ,@body))
DO-W
> (do-w `(:a ,arg) "arg")

おーできた。

> (do-w `(:a ,arg) "arg")
(:A "arg")
> (do-w x "abc")
(:A "abc")

うまく動いている気がする。

slimv と slime-vim 使ってみたものの

swank サーバを立てるのは簡単

これはたとえば ubuntu なら apt install cl-swank とかして /usr/share/common-lisp/source/slime/start-swank.lisp を動かせばよい。clisp でも sbcl でもどちらでもよい。というか通常は自動起動するから(vimemacs から) そもそも起動しなくてもよい。

slime-vim

まず vim 用の slime が必要だ。slimv と slime-vim というのがあるらしい。後者はどうやら tmux などのコピー&ペースト機能を利用して repl に対して文字列を送る。swank サーバに対応しているかどうはわからない。対応してないと判断してつかうのはやめた。シンプルなんだけど、tmux で切り替える手間や、コピペに失敗する?みたいで tmux がときどき乱れるので安定性に欠く。

slimv

ということで slimv 。どうも、インストール方法で動いたり動かなかったりするみたいだ。私は単純に .vim/plugin に github から clone した。README.txt にはこれでよいと書いてある。ところが動かない。これ設定になんかコツが必要みたいだ。SlimvInitBuffer がないといって怒られる。

ところが SlimvInitBuffer はちゃんと ftplugin/slimv.vim で定義されている。ということでこれはおかしい。そこで、ftplugin//.vim の最後で call SlimvInitBuffer() しているところをコメントアウト。.vimrc のコメントアウトは "" でくくるのね。

なんか様子がおかしいので lisp 以外の clojure r schemeディレクトリをざっくり削除。気分はブラックジャック。そして

let g:slimv_swank_cmd = '! tmux new-window -d -n REPL-CLISP "clisp /usr/share/common-lisp/source/slime/start-swank.lisp "'

を追加。slimv の README.txt にいくつかサンプルが載っている。tmux を起動して vi ~/tmp/test.lisp を編集。 call SlimvInitBuffer() を実行。そして , + b とするとサーバが立ち上がった。TCP の名前つきの socket (だっけ?) でコネクトしている模様。

, + e などで一行だけの評価可能。エラーを起こすと lisp が死ぬ。まぁここまではいいとして、、、

slimv をつかうと、作者の好みの設定に強引させられてしまう。まず ()の自動挿入。非常に鬱陶しい。これは let g:paredit_mode = 0 で回避できた。さらに Hyperspec (だっけ?) が勝手に Help してくれる。非常に鬱陶しい。考えの邪魔になる。これはどうやっても抑制できなかった。

画面が2つに分かれるのも気に食わない。

f:id:ryos36:20190227164828p:plain
slimv による起動

ということで、使うのをやめました。当面 tmux で十分という事で。swank サーバはいいアイデアなんだけどな。

複雑な(?)マクロ

  • まずは目標
"abc" と `(:tag ,aa)  -> (:tag "abc")

としたい。 簡単には

(defun f (arg) `(:tag ,arg))

これでいいのか、、、。でも defun とかが冗長。

(defmacro m0 (name arg tlst) `(defun ,name (,arg) ,tlst))

 (m0 ff arg `(:tag ,arg))

 (ff "abc")
(:TAG "abc")

なんとなく関数を作るマクロが出来た。lambda にしてみる。

(defmacro m00 (arg tlst) `#'(lambda (,arg) ,tlst))

 (funcall (m00 arg `(:tag ,arg)) "abc")
(:TAG "abc")

できた。arg が冗長。

(defmacro m00 (tlst) `#'(lambda ,(remove nil (mapcar #'(lambda (i) (if (and (listp i) (eq (car i) 'SYSTEM::UNQUOTE)) (cadr i))) (cadr tlst))) ,tlst))

(m00 `(:tag ,arg))
#<FUNCTION :LAMBDA (ARG) `(:TAG ,ARG)>

(funcall (m00 `(:tag ,arg)) "abc")
(:TAG "abc")

より複雑なのが出来た。SYSTEM::UNIQUOTE は他の lisp で他の大丈夫か不安。 データを作る。

(defparameter *my-list*
   '((:tag . (m00 `(:tag ,arg)))
     (:tag2 . (m00 `(:tag2 :local-tag "local" ,arg)))))

(setf input-data '(("abc" . :tag) ("def" . :tag2)))

(funcall (eval (cdr (assoc :tag *my-list*))) "abc")
(:TAG "abc")

input-data を評価する関数を作る。

(defun eval-input-data (lst) (mapcar #'(lambda (x) (let ((word (car x)) (
tag (cdr x))) (funcall (eval (cdr (assoc tag *my-list*))) word))) lst))

(eval-input-data input-data )
((:TAG "abc") (:TAG2 :LOCAL-TAG "local" "def"))

なんとなくできた。毎回 eval するのは馬鹿らしいので cache する。

(defun eval-input-data (lst) (mapcar #'(lambda (x) (let ((word (car x)) (
tag (cdr x))) (let* ((func (cdr (assoc tag *cached-my-list*))) (new-func (if fun
c func (eval (cdr (assoc tag *my-list*)))))) (if (null func) (push `(,tag . ,new
-func) *cached-my-list*)) (funcall new-func word)))) lst))

(defparameter *cached-my-list* nil)

*cached-my-list*
NIL

(eval-input-data input-data )
((:TAG "abc") (:TAG2 :LOCAL-TAG "local" "def"))

 *cached-my-list*
((:TAG2 . #<FUNCTION :LAMBDA (ARG) `(:TAG2 :LOCAL-TAG "local" ,ARG)>)
 (:TAG . #<FUNCTION :LAMBDA (ARG) `(:TAG ,ARG)>))

(eval-input-data '(("fff" . :tag)))
((:TAG "fff"))

なんかできている気がする。 my-list の m00 が余計だ。なんとかならないのか?これはまた考える。

asdf を久々に使う

common lisp の環境も関係者の努力によっていろんなツールが着々とバージョンアップを重ねているらしく、しらないうちに adsf も version 3.0 になった。3年以上前のようだが、、、 ドキュメントも充実し始めているので、もはや断片的に誰かの blog や書き残し(多分古い情報だ)をみるより本家を見た用がよい。そのうちこの blog も古くなる。 日本語の(非公式らしい)訳があるので参考になる。

Top (ASDF マニュアル)

2014 と少し古いようが keens さんの blog も参考になる。

require, ASDF, quicklispを正しく使う | κeenのHappy Hacκing Blog

まず version 2.0 のスタイル asdf:central-registry は使わない。後方互換もあるようなので使っても構わないが、3.0 以降では非推奨だ。

ubuntu では簡単に apt install cl-asdf でインストールできる。そうすると、/usr/share/common-lisp/ にインストールされる。source と systems があるが systems は古いスタイルようなので無視する(asdシンボリックリンクがある)。source が対象になる。asdf は /usr/share/common-lisp/cl-asdf にあり、さらに自動的に build ディレクトリの下に asdf.lisp があるはずだ。これを load すれば以後 asdf が使える。よかったよかった。

clisp では

#-:asdf (load "/usr/share/common-lisp/source/cl-asdf/asdf.lisp")

と書けばいいだろう。これで asdf が使える。asdf:asdf-version でバージョンを確認しよう。

[1]> (asdf:asdf-version)
"3.3.1"

つぎに追加のディレクトリ。

ASDFがシステムを見つけられるように設定する (ASDF マニュアル) にあるように、

  • ~/common-lisp/
  • ~/.local/share/common-lisp/source/

のいずれかにソースを置くようにする。そうすると自動的に検索対象になるようだ。追加の為には

~/.config/common-lisp/source-registry.conf.d/

に 50-luser-lisp.conf などと conf の拡張子のあるファイルを置いていく。私は、keens さんを参考に3つのパスを通した。

> ls
10-systems.conf  20-ryos.conf  30-quicklisp.conf
> cat *
(:tree "/usr/share/common-lisp/source/")
(:tree (:home "Lisp/"))
(:tree "~/quicklisp/dists/")

最初の /usr/share/common-lisp/source は ubuntu における common-lisp ライブラリの標準のインストール場所。以後 apt などで install されたライブラリがここで参照できる。

そしてこれまた人まねで quicklisp の初期化は .clisprc からコメントアウトした。(sbcl 使えって?)、これでうまくいくはず。いままでいい加減に load とかやってたのを ubuntu の世界でうまく整理できた。

cygwin もやってみた。asdf を make して build に asdf.lisp が出来るところまでやったが、(require "cl-fad") (公式ドキュメントによると "cl-fad" と文字列でやるのが互換性のある記述らしい。'cl-fad は GNU lisp で動くが互換性はないとのこと)、とすると alexandria がこける。どうも LANGの設定が絡んでいるようだが、なにが悪いのかは特定できなかった。cygwinclisp がだめなのかも。

証明書の管理は2つある

コンピュータ証明書の管理とユーザ証明書管理って微妙に違う。PowerShell から certmgr を起動せよと書いてあるのだが、それを読み飛ばして cert... で検索して実行しようとすると、、、 f:id:ryos36:20190107114004p:plain この絵では2つ出ているが(たぶん一回ユーザ証明書を起動したから)、最初はコンピュータ証明書しか選択肢になかった。起動するとこんな感じ。

f:id:ryos36:20190107114213p:plain

いまになってみると微妙な違いが分かるが、上の絵はローカルコンピュータになっている。 次にユーザ証明管理を起動するとこんな感じ。

f:id:ryos36:20190107114350p:plain

微妙に違う。そして個人と書かれたフォルダは別々のものが入っている。コンピュータ証明書の方からは Azure が必要とするクライアント証明書は入っていないので注意が必要。 f:id:ryos36:20190107114935p:plain

VPN を他の PC にインストール

もういっこククライアント証明書をつくってエクスポートしました。 そして、Azure からダウンロードした VPN の exe を 32bit Windows 10 にインストール。まずは証明書なしでアクセス。当然、証明書がないと怒られます(絵がなくてすいません)。 その後、クライアント証明書をインストール。ルート証明書を Azure 側にインストールした後ですので、Azure はこのクライアント証明書を知りません。さてアクセス。が~ん。セキュリティチェックに引っかかって接続できません。TLS のバージョンの問題かと思いましたが、ちゃんと 1.2 でアクセスしているもよう。

VPN - Error 812

です。しょうがないのでとにかく Windows10 をアップデート。最新にしてみます。それになんと2日かかりました。でもって今アクセスするとちゃんと VPN つながりました。古い Windows10 はセキュリティのチェックにひかかって Azure の VPN にはアクセスできないようです。