標準愚痴出力

個人的なIT作業ログです。もしかしたら一般的に参考になることが書いているかもしれません(弱気

直近の Lisp 活動 (gmnlisp v0.7.0 and Smake v0.4.1)

gmnlisp v0.6.0 で出来ていなかったこと:

  • 既存エラーのLispオブジェクト化
    • 対応エラー追加:<undefined-function> スロットを参照する包括関数 (undefined-entity-name), (undefined-entity-namespace) も対応 → まだまだ修正は続く
  • データと関数の名前空間が同じ → 対応!
    • (lambda), (function), #' は関数それ自体ではなく、関数への参照を返すようにした
    • (funcall), (map*), (apply) は関数ではなく参照を要求し、関数自体が与えられた時はエラーを起すようにした。

(function), #', (funcall) の対応は相当の難事業になるかなと思っていたら、意外とあっさりできてしまいました。ですが、 今まで一つの名前空間だった変数と関数を分離したことで互換性的にかなり問題が出ていることが予想されました。 ためしに Smake をリビルドしてみました。

  1. エラーオブジェクト gmnlisp.ErrExpectedString がリンクエラー
    • 例外処理の大改造したので、これは予想の範囲 → すぐ直った。
  2. (apply #'make … ) が期待どおり動かない
    • !?

2. は、どうも仕組み的にどうしようもなく、逆に今まで動作していたのがおかしかったようです ( 規格上 #' は制御構文的な関数を引数にとった時の動作は未定義とされている )

ということで、(apply #'make …) はあきらめざるをえないのですが、そうなると動的に TARGET: SOURCES... のエントリが作れないので、代替手段を作る必要があります。

ということで (updatep TARGET SOURCES...) という関数を作りました。ファイルTARGETより新しいソースSOURCESの要素を返す関数です。SOURCESの要素は文字列、もしくは文字列を格納したリストです。こんな感じで使います。

(defglobal EXE     (shell "go env GOEXE"))
(defglobal NAME    (notdir (getwd)))
(defglobal TARGET  (string-append NAME EXE))
(defglobal SOURCE  (wildcard "*.go"))

(if (updatep TARGET "Makefile.lsp" "embed.lsp" "go.mod" "go.sum" SOURCE)
  (progn
    (sh "go fmt")
    (sh "go build")))

これと、ISLisp 標準関数 (case) を組み合わせれば、もう(make)関数はいらないかなと思います。

(case $1
  (("clean")
   (dolist (obj (wildcard "*.o"))
     (rm obj))
   (rm "a.out"))
  ; 以下省略
  )

これらの修正を反映したバージョンは次のとおりです。