C言語でLISP処理系を作ってスクリーンエディタに組み込んでみた
この記事は,拙作記事『シェルスクリプトでLISP処理系を作ってみた』および『JavaScriptでWebブラウザ版LISP処理系を作ってみた』の姉妹版です.
筆者が簡易実装している純LISP処理系シリーズでは,シェルスクリプト版がコマンドラインREPL,JavaScript版がHTMLテキスト入出力をユーザインタフェースにしています.ですが,LISP系言語ではやはりS式評価や括弧対応の機能をもつスクリーンエディタが欲しい.でも,簡易実装の当作にGNU Emacsを併用するのは大げさだし,rlwrapの括弧対応だけでは心もとない.さて,何かないか…と考えていたところ,ありました,有名な簡易実装スクリーンエディタ『Kilo』が.
ということで(?),Kiloに組み込むためだけにオリジナルのPureLISP.shをC言語で書き直し,更に,GNU Emacsでいうeval-last-sexp
(や括弧対応機能)相当をKilo側に独自に追加して今回のLISP処理系と連動するようにしてみました.とりあえず『Kilo-PureLISP』と呼んでいます(コマンド名はkplisp
).
- Docker image:
docker run --rm -it ytaki0801/kplisp
なお,実際に組み込んでみたKiloは,チュートリアルサイトのStep 130までのソースコードです.この規模の処理系なら,検索や構文ハイライトの機能は要らないかなと.もともとLISP体験用・教育研究用のための純LISP処理系として開発し,あらゆるコンピュータ環境で利用できるようにすることを志向しているため,機能限定の側面がある次第です.
eval-last-sexp
相当の機能の実装
括弧対応およびいずれもLISP処理系側で必要な機能なので,本来であればLISPで実装するのが筋なのですが,そのためには,Kilo側の様々な機能をAPI化してLISP側から呼び出せるようにする必要があり,それはとても面倒煩雑であるため,今回はKilo側にC言語で機能追加しました.なお,機能追加部分はKilo本体とは別のCファイルとして記述しており,構成の概要は次の通りです.
-
lastSExp
:現在のカーソルの位置から前に向かって1文字ずつチェックし,最後の括弧対応がとれる位置を探し出して返す関数. -
matchParentheses
:lastSExp
を呼び出して最後の括弧対応がとれる位置を取得し,その位置に一旦カーソルを移動させ,0.3秒後に元の位置に戻す関数.『)
』が入力されるたびに呼び出される. -
evalLastSExp
:lastSExp
を呼び出して最後の括弧対応がとれる位置を取得し,現在のカーソルの位置からその位置までの文字列をコピーする.コピーした文字列はLISP処理系に渡して評価を行い,評価結果の文字列をミニバッファ(ステータス表示部)に出力する.
なお,組み込んでいるLISP処理系のマクロ文字相当がquote
のシングルクォーテーションしかないため,そこだけ行きあたりばったりの追加読込を行って評価に回しています.
備考
記事に関する補足
既存のLISP処理系のKiloへの組込み
実のところ,当作の純LISP処理系の前に,いくつかの既存のLISP処理系(Gauche,GNU Guile,Chibi-Scheme,Embeddable Common Lisp)のKiloへの組込みを試みています.
文字列ベースのS式評価連動が必ずしも簡単にいくわけではないため,部分的・実験的な実装に留まっています.処理系APIをC言語から利用する場合の記述例という程度の観点で上記の通り公開しています.
GNU EmacsはLISP系言語専用の時代なのか?
いきなり爆弾を落としますが,あくまで実用面での傾向や考察というかそんな感じの駄文です.
各種言語処理系とも連動するカスタマイズ性の高いテキストエディタとしてはGNU Emacsが長らく君臨していましたが(と書きつつ,筆者はvi&シェルもよく使いますが),ここ数年の間に,VSCodeやAtomが同等以上の開発環境ツールとして急速に普及しています.GUI全盛のイマドキではさもありなん,ではあるのですが,GNU Emacsがキーバインド前提やターミナル志向の印象を拭えず,viほどではないものの,敷居の高さを維持してしまったのも理由にあるかと思います.あと,やはり括弧だらけの謎言語でカスタマイズするというのが世間的には(以下自虐的につき省略).
ただ,やはりLISP系言語による開発ではGNU Emacsが圧倒的に優位です.括弧対応だけならあらゆるテキストエディタが対応していますが,S式の各種評価機能は,他の系統のプログラミング言語にはあまり見られない,部分実行等が可能なLISP系言語の特徴ならではの必須機能かと思います.特に,eval-last-sexp
.当初はあれのためだけに筆者作の純LISP処理系をscheme-mode
流用で使っていたという.そういう意味では,GNU Emacsがライブラリ化されて各種LISP処理系に組み込めるようになればいいんですけど,Emacs Lispを切り離すことは不可能っぽいかな….
eval-last-sexp
相当の実装
他のエディタにおける上記のように書いたものの,今回の実装に関する解説の通り,eval-last-sexp
の処理方法はそう難しくはありません.というか,どのエディタでも括弧対応機能はデフォルトなので,その実装を流用すればさっくりできるような.特に,VSCode.インタプリタを起動しているターミナルに向けた『選択したテキストの実行』というのは既にあるし.拡張機能の実装方法調べてみるかな….
更新履歴
- 2021-04-24:実装まわりの解説と補足を追加.
- 2021-04-18:初版公開
Discussion