misakiではプロジェクト外の*.cljファイルをリードしてHTMLに変換しているのですが、 そのcljファイルにエラーがあったとしても今まではmisaki内の評価関数内のエラーとして 例外が発生してしまって、具体的なエラー箇所がぱっとわかる状態ではありませんでした。
解決策†
これを解決するためにclojure.core/readで使っているjava.io.PushbackReaderを
プロキシする形で行数をカウントするようにしてみました。
(defn create-pushback-reader-with-line [in]
(let [line-num (atom 1)]
(proxy [PushbackReader IDeref] [in]
(read [] (let [c (proxy-super read)]
(if (and (not= -1 c) (= \newline (char c)))
(swap! line-num inc))
c))
(deref [] @line-num))))readをオーバーライドして改行があった場合にline-numをインクリメントしているだけです。
clojure.lang.IDerefを継承しているのはderefでカウントした行数を取得するためです。
(derefを選んだのはなんとなくアクセスが楽だったので)
(require '[clojure.java.io :as io])
(let [r (io/reader "file-path.clj")
pbr (create-pushback-reader-with-line r)]
; 通常のread
(println (.read pbr))
; 行数を取得
(println @pbr))これで各S式がファイルのどの行数に書かれているかをリード時に把握できようになったので
あとは以下のようにclojure.core/readのエラー時に行番号を利用するだけです。
(let [file-path "foo.clj"
pbr (create-pushback-reader-with-line (io/reader file-path))
line-num @pbr]
(try
(read pbr)
(catch Exception ex
(println "エラー箇所は" file-path "の" line-num "行目です!!"))))上記の例は実際にはEOFまでループしなければ意味がないのですが、 そこまで書くと肝心の部分が見づらくなるので省略しました。
なおあくまでもリード時の処理だけなので、評価時のエラー(例えば未定義変数へのアクセス)は 評価関数内のエラーとして扱われます。
また私が他の方法を知らないのでこういった方法で対応しましたが、 もし他にスマートな方法があれば是非教えてください!
最後に†
misakiのdevブランチには上記対応を反映済みなので、具体的にエラーにどう反映させているかなどは devブランチのsrc/misaki/reader.cljをご参照ください。
