ラベル Emacs の投稿を表示しています。 すべての投稿を表示
ラベル Emacs の投稿を表示しています。 すべての投稿を表示

2014年1月13日月曜日

WandboxをEmacsから操作するライブラリを作った

遅めの年始のご挨拶。あけましておめでとうございます。

オンラインコンパイラ Wandbox を Emacs から操作するライブラリを作ってみました。最初のコードを書いたのは11月なのですが、修正するうちにだいぶ使い勝手が良くなってきたので、gist から GitHub に移動しました。
インストールすることで、各種コンパイラをインストールせずに、Emacs 上でコード片を実行することができるようになります。当然ですがネット環境が必要です。

インタラクティブな操作 (M-x) もできますが、*scratch* バッファなどで wandbox-compile 関数を使った方が細かいコンパイル指定ができるのでオススメです。使い方は付属の README を参照してください。

おまけ機能として、引数を Common Lisp (CLISP) の S 式として実行するためのマクロ wandbox-eval-with なんてものも用意しています。wandbox-eval-region と似たような機能ですが、最後の S 式の結果も出力されるようになっています。:stdin などの追加オプションも指定可能です。ただし Elisp にない構文など(文字リテラル #\a とか)は引数に使えません(出力はOK)。

;;; *scratch*
(wandbox-eval-with (:stdin "HELLO\nWorld")
  (do ((c #1=(read-char nil nil :eof) #1#))
      ((eq c :eof))
    (format t "~s " c))
  )

;;; *Wandbox Output*
<<< program_message >>>
#\H #\E #\L #\L #\O #\Newline #\W #\o #\r #\l #\d
NIL

<<< status >>>
0

2010年9月8日水曜日

[Emacs]Unicodeエスケープ文字を復元する

\u0040\u006b\u006f\u0073\u0068\u005f\u0062\u006f\u0074
上記のようなUnicodeエスケープ文字を元の文字列に戻すためのEmacs Lispです。ついでなので逆のエスケープする関数も書いておきます。

xyzzyの方は単純にElispのコードを書き換えただけ。xyzzyはemacsより書き方に幅があるようだけど、あんまりトリッキーなコードだとelisp<=>xyzzy間のコードの移植が大変になりそう。

emacsでこんな感じの置換操作を行う場合は以下のように書くのが定石らしい。

(while (re-search-forward "検索したい文字列" nil t)
  (replace-match "置換後の文字列" nil 'リテラルかどうか))

xyzzyならストリームを扱う関数を作ってed::text-decode-regionから呼び出すのが便利。

参考リンク

2010年5月31日月曜日

[Emacs]sha1-file

舌足らずなブログ: あれ? がようやく解決したのでメモ。

こっちもxyzzyのエンコーディング指定と同じミスでした。
sha1.elはそれなりに長い文字列を受け取ると外部プロセスに処理を投げるので、その時の入力をバイナリ指定にしないといけないらしい。

(defun sha1-file (filename)
  (require 'sha1)
  (let ((coding-system-for-write 'binary))
    (with-temp-buffer
      (insert-file-contents filename)
      (sha1 (buffer-string)))))

ちなみにEmacs内部で処理する場合はエンコーディング指定は要らない。

(let ((sha1-use-external nil))
  (with-temp-buffer
    (insert-file-contents FILENAME)
    (sha1 (buffer-string))))

2010年2月1日月曜日

[xyzzy]zone.l移植

Emacsのジョークプログラムzone.elを移植したのでGitHubに置いておきますね。

壁||´・ω・`)っ kosh04/xyzzy-lisp/site-lisp/zone.l

※コード内の説明文はシフトJISなのでブラウザから開くとたぶん文字化けしますん。

2009年11月30日月曜日

[Emacs]eldocをほんの少し便利にする

LISPファイルの編集時に関数の引数を表示するマイナーモードとして、Emacsではeldoc-mode、CLではslime-autodoc-modeがあるんですが、両者の表示形式が微妙に異なることが気になったのでついカッとなってeldocの方を軽く改造しました。反省はしていない。

gist: 244937 - GitHub

参考画像を見れば分かると思いますが、ポイント位置の引数の強調方法がデフォル トでは太字ではなくハイライトになってます。

Eldocの表示デモ

その他、関数・変数ともにdocstringの表示が可能です。

ソースコードはGistに置いておくのでinstall-elispからインストールすればCL(SLIME)使いは幸せになれるかもしれません。

(install-elisp-from-gist 244937 "eldoc.el")

動作環境

Emacs22以上で動作確認。

無駄にCLのマクロを使っていますがバイトコンパイルすれば (require 'cl) がない状態でも使えるので安心です。


ここまで書いておいてあれですが、このeldoc.elの改造はミニバッファ表示方法がちょこっと変わった位のものなので特に目新しい機能はありません。

単に表示色を変更したいだけなら本来のeldocのfaceを弄ればそれっぽくなります。

(set-face-background 'eldoc-highlight-function-argument "darkseagreen2")
(set-face-bold-p 'eldoc-highlight-function-argument nil)

2009年11月11日水曜日

[Emacs]そんなことよりeval-after-load使おうぜ

もっと使われてもいいのにと思うEmacsの設定周りのメモ。

[関数] (eval-after-load FILE FORM)

この関数は、指定されたライブラリFILEがロードされているときにのみ2番目の引数FORMを評価するというもの。

例えばmigemoをロードする時の自分の設定はこんな感じ。

;; migemoを読み込む。存在しない場合はnilを返す
(load "migemo" 'noerror)

(eval-after-load "migemo"
 (quote
  (progn
    ;; この辺がmigemoをロードした後に評価される
    (defadvice isearch-mode (before migemo-off activate)
      "初期状態は常にnil"
      (setq migemo-isearch-enable-p nil))
    (fset 'toggle-migemo #'migemo-toggle-isearch-enable)
    (define-key isearch-mode-map (kbd "C-t") 'migemo-isearch-toggle-migemo)
    )))

この関数の利点は

  • ライブラリが存在しない場合はeval-after内のフォームが評価されない。
    つまりエラーにならない。
  • emacsの標準関数である (emacs/lisp/subr.el内で定義)。
    つまり自前で関数を用意しなくて良い。
  • 一度eval-after-loadが評価されれば、後からライブラリが読み込まれてもフォーム内部を評価してくれる。
    これは各種メジャーモードのフックに関数を引っ掛けて、フォームの評価を遅らせるのと同様の手段となる。
    例: (add-hook 'emacs-lisp-mode-hook 'turn-on-eldoc-mode)

逆に不便な点、気にくわない点は

  • 評価できるフォームが1つしか指定できない。
    => (progn ...) を使えば複数フォームを指定できる
  • 関数なので引数が必ず評価される。そのためにフォームをquoteするのが嫌。おまけにシングルクオート '(...) を使うとフォーム内のインデントが崩れる。
    => 代わりに (quote FORM) を使えば長いフォームもそれなりにインデントされる。

この辺は一応マクロを使えば解決するが...なんで関数なんでしょうね、これ。

(defmacro eval-after-load-1 (file &rest args)
 `(eval-after-load ,file
    (quote (progn ,@args))))
(put 'eval-after-load-1 'lisp-indent-function 1)

参考URL

2009年6月3日水曜日

[xyzzy]regexp-optを移植

Emacsのregexp-opt.elをxyzzyに移植してみました。

一応Emacs移植キットは使わずに自前で動くようにしてあります。 あまり実用的な出来ではないですが、よかったらどうぞ。

https://github.com/kosh04/xyzzy-lisp/tree -> site-lisp/ -> regexp-opt.l

から入手できます。

regexp-opt.elとは?:

GNU Emacs Lispリファレンスマニュアル: 探索と一致
 -- Function: regexp-opt STRINGS &optional PAREN
    この関数は、文字列STRINGSのいずれかに一致する効率よい正規表現を返
    す。これは、たとえばフォントロック(font-lock)モードなどで、可能
    な限り高速な一致や探索を行う必要がある場合に有用である。

使い方:

regexp-opt.lを$XYZZYHOME/site-lisp/ディレクトリ内に置いてバイトコンパイル。 .xyzzyに(require "regexp-opt")と書いてxyzzyを再起動する。

コード例:

(regexp-opt '("aa" "ab" "ba" "bb"))
;;=> "a[ab]\\|b[ab]"

(regexp-opt '("defun" "lambda"))
;;=> "defun\\|lambda"

(regexp-opt '("defun" "defsubst" "defmacro" "defalias" "defvar" "defconst") t)
;;=> "\\(def\\(?:alias\\|const\\|macro\\|subst\\|un\\|var\\)\\)"
バグ・問題点:
  • Emacsが返す値と違う(というより冗長な)場合がある
    (regexp-opt '("define" "lambda" "fn" "define-macro" "lambda-macro") 'words)
    ;;=> "\\<\\(define\\(?:-macro\\|\\)\\|fn\\|lambda\\(?:-macro\\)?\\)\\>" [xyzzy]
    
    (regexp-opt '("define" "lambda" "fn" "define-macro" "lambda-macro") 'words)
    ;;=> "\\<\\(define\\(?:-macro\\)?\\|fn\\|lambda\\(?:-macro\\)?\\)\\>" [Emacs]
  • 引数の文字列リストが長すぎると、明らかに遅くなる
  • マクロのfsetって大丈夫? (si:*fset 'save-match-data #'ed::protect-match-data)

2009年4月19日日曜日

GitHubアカウントをとりました

いくつかファイルを置いておいたので、よかったらどうぞ。

GitHubアカウント - github.com/kosh04

  • xyzzy/
    • etc/
      • newLISP - newlisp-mode用キーワードファイル
    • site-lisp/
      • cl.l - xyzzyにないCL関数の移植のようなもの
      • corman_search.l - CLのsearch関数移植
      • cygwin.l - 主にmanコマンドを利用するため
      • newlisp.l - newLISP編集用メジャーモード
      • redef.l - xyzzy組み込み関数を再定義したもの
      • util.l - 自作関数など
  • emacs-lisp/
    • xyzzy.el - xyzzy/CommonLisp/SLIMEの便利そうな関数を色々移植したもの
  • newlisp-files/
    • init.lsp - CLっぽい関数とか
    • newlisp.el - newLISP編集用メジャーモード

真面目にファイルの配布するならライセンスやマニュアルも考えるべきなんだろ うけど、今日はとりあえずここまで。gitに慣れるのに手間取って疲れた…

[追記]

GitHubのwebページがUTF-8らしいので、それに伴いファイルの文字コードも一部 UTF-8になっています。で、xyzzyだとファイルのコンパイル・ロード用の関数が デフォルトではShift_JISしか対応しないようなので、compile-file/loadの代わ りにmc-compile-file/mc-load-file関数などを使って明示的に文字コードを指定 してくだしあ。

後でREADMEくらいは作ろう。

[追記 (2009-12-08)]

ブラウザの文字化けよりもライブラリを実際に使う時の利便を考慮して、元のsjisに戻しました。

2009年4月6日月曜日

[Emacs] シンボルから info を呼び出す

指定したシンボルに対応した Info を表示するには `info-lookup-symbol' を使えばいいのか。なるほど。

Elisp 用の Info は標準で入っているので、とりあえず C ライブラリのだけでも入れて おくことにする。以下、その手順。

  1. libc の info を持ってくる
    http://www.gnu.org/software/libc/manual/
    http://www.gnu.org/software/libc/manual/info/libc-info.tar.gz
  2. 適当なディレクトリに解凍する
    $ mkdir -p ~/info/libc
    $ tar xzvf libc-info.tar.gz.tar -C ~/info/libc
  3. .emacs に追加
    (require 'info)
    (add-to-list 'Info-additional-directory-list "~/info/libc/")
    ;; 標準では C-h S, <f1> S または M-x help S
    (global-set-key "\C-ch" 'info-lookup-symbol)
  4. Info-directory-list のディレクトリ内にあるファイル dir に以下の一行追加
    * libc: (libc).         The GNU C Library
  5. 各モードで info-lookup-symbol を呼び出して ウマー (゜Д゜)
    # c-mode では初回関数呼び出し時に明示的にモードを指定する必要あり

特に c-mode と libc-info の関連付けを指定してないけど、 Emacs 側で勝手にマー ジしてくれるらしくこのままで使えるみたい。

でも、もっと多機能なものが既にあるからこのメモはあんまり当てにならないかもね。

2009年3月24日火曜日

[Elisp] font-lock の勉強中

newLISP を Emacs から弄るなら、関数・シンボルなどのキーワードの色付けは 必須項目な訳で、しぶしぶ Google 先生と $EMACS/lisp 内のファイル群を頼り に font-lock について調べています。

xyzzy ならキーワードファイルに単語を放り込んでハッシュで勝手に色分けして くれるのが楽チンだったんだが…

それはよしとして、Elispの色付けといえばこれが便利。

Lisp 関数だけに色付け http://www.bookshelf.jp/cgi-bin/goto.cgi?file=meadow&node=font-lock-func

で、紹介されている Elisp のソースをよく見たら、関数定義などは内部で呼び 出している関数が一部違うだけで重複している部分が結構あることに最近気づき ました。

「これはマクロの出番だ」と思い立って書き直してみたら、見事に(?)行数 1/2 ほどのカットに成功したようです。

http://gist.github.com/83749

[2009-04-12T18:44:56+09:00] typo に気づいたので修正

http://gist.github.com/93934

動作に変化はありませんが、プログラムの可読性が少しだけ上がった気がするの でここに残しておきます。

2009年3月11日水曜日

本家にばれてた

以前書いたEmacs用のnewlisp-modeが本家様に気づかれたようです。さすがマイナー言語。

http://www.newlisp.org/code/newlisp.el

というかこれならシンボル補完とか色付けとかもっと頑張ればよかった。 最近newLISP弄るのはもっぱらxyzzyからだしなあ。

SLIME+newLISPを完成させるか。。。

2009年2月24日火曜日

[Emacs]tabba.elのせいだったのか

tabbar-modeを使うと基本的に「M-x b」等は通常操作と同じ一つ前に開いたバッファにもどります。ただ、kill-bufferしたときだけグループ化された前のファイルに戻ります。

Elisp/タブでバッファを切り替える

どうりでkill-bufferしたとき元のバッファに戻らないわけだ。。 パッチも紹介されているが、どうやらフック関数のコメントアウトだけなのでelispで修正できる範囲のよう。

(remove-hook 'kill-buffer-hook 'tabbar-buffer-kill-buffer-hook)

直った直った。すっきりした。

ちなみにtabbar.elの設定

(require 'tabbar)
(tabbar-mode (if window-system 1 -1))
(remove-hook 'kill-buffer-hook 'tabbar-buffer-kill-buffer-hook)
(when tabbar-mode
  (global-set-key [?\C-x ?\C-.] 'tabbar-forward)
  (global-set-key [?\C-x ?\C-,] 'tabbar-backward))

2008年12月18日木曜日

おおえまくすよ マクロをつかわないとは なさけない

(require 'cl) は悪なのか?の続き。

気が向いたので軽く調べてみた。

---

GNU Emacs のマニュアルには以下のような注意書きがある。

Common Lisp Extensions Overview
Please note: the CL functions are not standard parts of the Emacs Lisp name space, so it is legitimate for users to define them with other, conflicting meanings. To avoid conflicting with those user activities, we have a policy that packages installed in Emacs must not load CL at run time. (It is ok for them to load CL at compile time only, with eval-when-compile, and use the macros it provides.) If you are writing packages that you plan to distribute and invite widespread use for, you might want to observe the same rule.

どうやら Emacs 実行時の cl パッケージ読み込みは禁止されていて、コンパイル時のマクロ展開に限って読み込みを許可しているらしい。つまり (require 'cl) ではなくて (eval-when-compile (require 'cl)) を使いなさいってことか。

コンパイル時にマクロ展開をしなければならないのは Common Lisp でも同じだから、この点は納得。

the CL functions are not standard parts of the Emacs Lisp name space

これは「名前には接頭辞を付けろ」という elisp のコーディング規則に反しているから関数上書きしないように気をつけてね、そんな意味でしょうか。

んー、名前空間の話は cl パッケージを読み込んだ時点でユーザは承知していると思うんだが。

ありがたいことに elisp のマクロは CL のマクロと(ほぼ)同等の機能があるのだから、マクロによる抽象化によってソースコードが短く、より簡素になるなら利用しない手はない。見やすいコードとそうでないコード、どっちを書きたい?と質問するようなもの。

…というのが自分の意見です。

所詮 .emacs 位しか書いたことのない学生の意見ですから反論は認めます。mew の作者さんのように何かしらの elisp パッケージを作成した方は、きっとこの cl パッケージのおかげで互換性等々に苦労されたんだと思いますし。

まとめ、あるいは教訓:

  • cl パッケージを利用するファイルは、必ずコンパイルして実行時にそれに依存しない形にすること。(eval-when-compile (require 'cl)) 用法用量を守って正しく使いましょう。
  • マニュアルはなるべく最新のものを参照すること。OSS のマニュアルの邦訳がメンテナンスされないのはよくある話。

おまけ:

「マクロではなく cl.el の関数群を使いたいときはどうすれば?」と質問された時の Stallman 氏の返事

Installed Lisp packages must not use those functions.

2008年12月15日月曜日

[Emacs] newLISP 環境を構築してみたい

[追記@2009-04-20T03:29:05]
もう少し拡張したものを置いておきます。
http://github.com/kosh04/newlisp-files/tree/master


Emacs から newLISP のプロセスを操作する関数群を書いたのでちょっと醸してみます。

もともと xyzzylisp 用に作ったんだけど、やっぱり資産がある elisp の方が短くできてしまったので。

使い方:

  1. 以下の URL のコードを newlisp.el で保存して eval-buffer
    http://paste.lisp.org/display/72178
  2. 必要に応じて、以下の変数を書き換え。
    • newlisp バイナリのパス名: *newlisp-command*
    • プロセス間文字コード: *newlisp-process-coding-system*
    Windows インストーラから newLISP をインストールした場合は、環境変数 NEWLISPDIR が設定されているはずなのでパス名はそのままで大丈夫だと思います。 newlisp バイナリには utf-8 が使える版と使えない版があるので、ここではマルチバイト文字を扱える utf-8 版を推奨します。 →[xyzzy] newLISP 環境を構築してみたい
  3. 後のS式操作はカンでお願いします。スクラッチバッファで遊んだことがある人ならたぶん分かるでしょう。 主に使うのは newlisp-eval-last-sexpnewlisp-show-repl くらいでしょうか。

残念ながらハイライトなどのリッチな機能はありません。

あ...それと (require 'cl) が必要かもしれないです。


Emacs(VineLinux4.2) だとなぜか評価のタイミングがずれました。
(一回目の newlisp-eval-last-sexp では何も表示されないで、二回目でまとめて出力されてしまう)
NTEmacs では問題なく動いたので、 Emacs 側の問題?

(require 'cl) は悪なのか?

自分の中で Emacs Lisp の CL パッケージの評判があまりよろしくない印象なんだが、はっきりした理由を聞いたことがないなあ。

気が向いたらまとめのページが無いか詮索してみよう。

詮索した。

elisp にもラベル記法があったのか

(let ((print-circle t)
      (lst '(7 8 3 9 4 0 1 6 5 2)))
  (princ (list (sort lst #'<) lst))
  (terpri))
;-> ((0 1 2 3 4 5 6 . #1=(7 8 9)) #1#)
;=> t

((lambda (x)
   ((lambda (y1)
      ((lambda (&optional y2)
         ((lambda (z)
            (list x y1 y2 z))
          (car (cdr #1=(cdr #2='(1 (2) 3))))))
       (car (cdr #3=(car #1#)))))
    (car #3#)))
 (car #2#))
;=> (1 2 nil 3)

;; 上のコードは少し古い destructuring-bind のマクロ展開 (CL)
(macroexpand '(destructuring-bind (x (y1 &optional y2) z) '(1 (2) 3)
                (list x y1 y2 z)))

Emacs Lisp はかなり Common Lisp に近い仕様なんだなと改めて思った。
# もちろん動的スコープだとかクロージャが無いとかツッコミはあるけど :-)

しかしこの書き方 (#n#) の名称なんだっけ...


CLtL2, CLHS をパッと見たけど 「#n# 構文 (#n# syntax)」としか書いてなかった。

あと、影響を受けたのは CL より Maclisp 方言か。歴史知らなさ過ぎかもしれない。

2008年11月18日火曜日

[xyzzy] newLISP 環境を構築してみたい

※一応 Emacs 版もあります。

newLISP に付属のエディタが使いにくい。

やっぱり使い慣れたエディタがいいよね、ということで xyzzy から newLISP シェル (REPL) を操作する簡単な xyzzylisp を書いてみた。

(defvar *newlisp-exe*
  (merge-pathnames "newlisp.exe" (si:getenv "NEWLISPDIR")))

(defun run-newlisp ()
  (interactive)
  (let ((*eshell* (format nil "~A -C -w ~A"
                          *newlisp-exe* (default-directory)))
        (*shell-mode-hook*
         (list #'(lambda ()
                   (rename-buffer "*newLISP*"))))
        ;; プロセス間通信は UTF-8
        (*default-process-encoding* *encoding-utf8n*)
        ;; なくても多分動く
        (*default-fileio-encoding* *encoding-utf8n*))
    (shell)))

;;; elisp ならこんな感じか
(defun run-newlisp ()
  (interactive)
  (let ((default-process-coding-system '(utf-8 . utf-8)))
    (run-lisp (format "C:/PROGRA~1/newlisp/newlisp.exe -C -w %s"
                      (expand-file-name default-directory)))))

ただし複数行の式を書くときは [cmd]~[/cmd] タグを使うことに注意。これは仕様だから仕方ない。
Evaluating newLISP expressions - newLISP v.9.4.5 Users Manual and Reference

書いていて気づいたが、 newLISP では日本語処理に難があるので UTF-8 が使えるバージョンを入手する必要がある。
http://www.newlisp.org/downloads/UTF-8_win32/

;; 比較
;; win32 で標準インストールされる newlisp.exe
> (explode "はろー")
("\227" "\129" "\175" "\227" "\130" "\141" "\227" "\131" "\188")
> (length "はろー")
9
;; UTF-8 の使える newlisp.exe
> (explode "はろー")
("は" "ろ" "ー")
> (length "はろー")
9
> (utf8len "はろー")            ; UTF-8 用 length
3

でも reverse 関数には日本語が使えないので泣く泣く次のように書く必要がある。 メンドクサ

> (apply append (reverse (explode "はろー")))
"ーろは"

さーて、何をしようか。

2008年10月23日木曜日

elispでarglist関数っぽいの

「xyzzyでarglist関数っぽいの 」[その1][その2] のおまけ。

何でこんなの作ったんだろう…

;;; for Emacs Lisp
(defun arglist (def)
  (unless (keymapp def)
    (let ((argstr (car (help-split-fundoc (documentation def) def))))
      (if argstr
          (format "%s" (cdr (car (read-from-string argstr))))
        (help-function-arglist def)))))

(arglist 'arglist)        ; (def)
(arglist 'lambda)         ; "(ARGS [DOCSTRING] [INTERACTIVE] BODY)"
(arglist 'if)             ; "(COND THEN ELSE...)"
(arglist 'ctl-x-4-prefix) ; nil

参考にした関数: describe-function-1 (lisp/help-fns.el)

elisp では関数定義の引数とドキュメントに書かれている引数が異なる場合があるので、そのときは後者を優先します。ややこしいなあ。

(help-function-arglist 'lambda)
=> (&rest cdr)
(car (help-split-fundoc (documentation 'lambda) 'lambda))
=> "(lambda ARGS [DOCSTRING] [INTERACTIVE] BODY)"

2008-10-25T00:40:33+09:00 [追記]

read-from-whole-string 関数 (lisp/thingatpt.el ファイルで定義されている) は標準で使えるものでないので read-from-string に変更。