header

2016年7月6日水曜日

Nimlang の関数を Emacs から呼び出す②

結構時間があいてしまってあれなのですが、以前 Nimlang の関数を Emacs から呼び出す(途中までしかできてない) という記事を書いてそのまま放置だったけれど、Nim のバージョンが上がってから試したらすんなり動いたというのと、el-get に登録したので紹介します。(Nim の lambda_lifting に関するバグがなおったあと良くなったとおもいますが、詳しいバージョンや Issue 番号など忘れてしまいました)

おさらい

Emacs25 から追加される dynamic module という機能で C の関数を呼び出すという機能を利用して、C にコンパイル可能な Nim で Emacs 用の拡張を作るという試みです。

その他日本語記事:

確認している Emacs と Nim

以下のバージョンで確認しています。

  • emacs-version(master branch): 25.1.50.1
  • Nim version(devel branch): 0.14.02

インストール

el-get に登録したので`el-get-list-package` とかでインストールできます。dynamic module を利用した拡張は(MELPA などで簡単にインストールして使える形で提供しているのは)まだないので、他の Dynamic Module の拡張のやり方を真似しようかな思っているので MELPA にはまだ登録していません。なので、位置づけとしては、Nim で Emacs の拡張を書くお遊びパッケージという感じです。

使い方

インストールが終わったら、

(el-get 'sync 'nim-emacs-module)

でロードパスの登録と autoload を使えるようにして、M-x `nim-emacs-module-init`で~/.emacs.d/に nim-emacs-module ディレクトリを作ります。(確認してないけど、別に手動でも OK です)

あとは作りたいパッケージ名の名前でファイルを作ります。例えば sample-xxx 関数を持つなら、sample.nim というファイル名になります。ここでつけるファイル名が Nim では(Nim でいう)モジュール名になり、なおかつ Emacs 用の関数の prefix 名となります。(便宜上そうなるように設計してるだけで Emacs 側にそれを強制するルールはありませんが)

あとは開いたファイルで, M-x nim-emacs-module-insert-template で必要な設定がファイルに挿入されます。

現在の nim-emacs-module では以下のようなコードが挿入されます。 (挿入されるものは将来的に変更されるかもしれないので、こんなニュアンスなんだという理解のほうがいいかもです。)

# `plugin_is_GPL_compatible` indicates that its code is
# released under the GPL or compatible license; Emacs will refuse to
# load modules that don't export such a symbol.
{.emit:\"int plugin_is_GPL_compatible;\".}
import emacs_module # Primitive wrapper for emacs_module.h
import emextra      # Helper library
init(emacs)
# Example(Create a file named `mod_test.nim`):
emacs.defun(return_t, 1):
  env.intern(env, \"t\".cstring)
# Provide functions registered by above from here.
emacs.provide()
# How to test the example's code:
#
#   $ emacs -Q -L .
#
# and then in *scratch* buffer, evaluate those lines:
#
#   The file name ‘ mod-test ‘ is added as prefix name of each functions and
#   its package name.
#   (require 'mod-test)
#   (mod-test-return-t)

Nim では#がコメントなので実質数行のコードですが、なんとなく Emacs で拡張作ってる方には Nim を知らなくてもわかるのではと思います。

この例では、ファイル名が mod-test.nim で関数名が return_t、引数の数が1つの Emacs の関数`mod-test-return-t`が作られます。

コンパイルの方法は nim-mode で C-c C-c でコンパイルできるようにしていますが、M-x nim-emacs-module-compile でもコンパイルできるはずです。

コンパイル後は上記コメントのように mod-test パッケージを require して、関数を試すという感じです。

今後

Emacs の dynamic module 機能がまだ始まったばかりなので今後どうなるかわかりませんが、多言語で複数パッケージを管理するスキームができたら真似してみたいなぁとおもいます。あと C、Nim とも初心者なので、まだ Nim<->Emacs Module 間でのメモリの扱いがどうなるのかわからなくてプリミティブなテストしか作ってないので時間があればもうちょっとなんとかしたいなぁという感じです。(Pull Requests 大歓迎です)

2016年1月18日月曜日

Nimlang 用の Emacs おすすめ設定

Nimlang 公式?の emacs メジャーモードは nim-mode でコード補完や eldoc サポートはこのリポジトリに入っているのですが、それ以外にあったほうがよいものとか

※注意 nim-mode はすでにインストール済みを前提に書いています。

なにはなくとも nimsuggest

nimsuggest は nim 用の IDE(or text editor)支援ツールでこれがないとコード補完と eldoc の機能が使えないので、Emacs で nim を書くにはほぼ必須ツールです。

リンク先にインストール方法は書いているとおもいますが、nim 本体と同じディレクトリに git clone して nimsuggest のディレクトリで

nim e ./compile_without_nimble.nims

をすればよかったと思います。ちなみに.nims ファイルは nimscript の略です。ghq を使っている場合は、同.nims ファイルの–path:../nim を Nim にしないとダメでした

無事ビルドしたら以下を emacs に設定することで eldoc と company-mode が使えるようになります。

(setq nim-nimsuggest-path "nimsuggest のバイナリへのパス")
(add-to-list 'company-backends 'company-nim)

flycheck (MELPA からインストール可能)

https://github.com/ALSchwalm/flycheck-nim

特にそのままでも使えますが、前述の nimscript の機能が新しいので syntax チェック機能がまだ未対応のようので nims や nimble ファイルを書くのであれば今のところ下の設定で flycheck は無視したほうがいいかもしれません。(おそらく syntax チェックは公式がそのうち対応するので)

(defun my-turn-on-flycheck-nim ()
  (when (and buffer-file-name
             (not (member
                   (file-name-extension buffer-file-name)
                   '("nims" "nimble"))))
    (flycheck-mode t)))
(add-to-list 'nim-mode-map 'my-turn-on-flycheck-nim)

indent-guide (MELPA からインストール可能)

インデント構造を見やすくする拡張です。リンク先に screenshot があるのでそれを見たほうがわかりやすいです。 https://github.com/zk-phi/indent-guide

quickrun (MELPA からインストール可能)

自分が紹介しなくてもみなさん知っているかもしれないですが、ワンコマンドで現在ファイルを実行する拡張です。多(プログラミング)言語に対応していますが nim も入っています。 https://github.com/syohex/emacs-quickrun

おわり

自分が使っているのはだいたいこんな感じです。他に便利拡張が教えてください。

2016年1月13日水曜日

Emacs から ibus をトグルする拡張作った

Evil 使ってる時に Normal モードに入った時に日本語をオフしたかったので作った。普通に shell command で`ibus engine`呼び出してるだけだけど、あまり良さそうな情報がなかったので自分で書いた。ここから ダウンロードできます。

設定

(require 'ibus-toggle)
(define-key global-map (kbd "C-o") 'tibus-toggle)

tibus-toggle でトグルできます。

Evil から使うなら、自分はこうしてます。指定するキーボードは`ibus read-config` コマンドでみれます。

(add-hook 'evil-normal-state-entry-hook
          '(lambda () (tibus-set-engine "'xkb:us::eng'")))

1/18/2016 追記 カーソル手前の文字が日本語だったら mozc にする設定を思いつきました。便利かどうか謎ですがよかったら使ってみてください。

(require 'rx)
(add-hook 'evil-emacs-state-entry-hook
          '(lambda ()
             (let ((c (char-before (max (point) (point-min)))))
               (when (and c (string-match (rx (category japanese))
                                          (char-to-string c)))
                 (tibus-set-engine "'mozc-jp'")))))

感想

ホントは mozc から変更したかった。けどそういうオプションなさそうだったので妥協して ibus で操作することにした。mozc 便利だけどかゆいところに手が届かない感じだ。

Emacs の smie.el で nimlang 用のメジャーモードのインデントを改善した

nimlang について日本語で検索しても英語で検索してもあまり情報がないのであまりつかっている人はいないかもしれませんが、Emacs の nimlang 用のメジャーモードである nim-mode のインデントを smie.el を使って改善したので紹介します。あと SMIE についての使用感とか…

ただ、Emacs25.1 で追加予定の if-let 関数(subr-x.el の)を使っており、むりやり古いバージョンの Emacs で動かそうとした場合 24.4 までがテストが通る限界でした。なので 24.3 以下ではおそらく travis のテストでしか確認してませんがダメだと思います。

nim-mode をアップデートすれば自動的に使えるようになっています。(というか古い python.el から流用しているインデントのコードははぼなくなった) あと関係ないですが、nimsuggest という nim 用の ide 支援ツールを使うと eldoc で情報が取得できれば表示されるようになっているのでインストールしたほうがいいです。

以降は smie について書きます。nim の構文は例として書きますが、nim については特に言及しません。

smie.el とは

説明なしでここまできてしまいましたが、emacs24.3(たぶん)で導入されたインデント計算エンジンです。

キータの記事 が非常にわかりやすかったです。

ざっくりいうと BNF っぽい書き方でインデント計算をよしなにやってくれるライブラリです。(実際は言語用のインデント数をあわせたり、デバッグでそんなにすんなりいかないかもしれませんが)

smie.el を使うメリット

以下のようなインデントを簡単に処理できます。

  • 例 1: condition の部分が長く折り返す場合

    if true or false and true or false
       true or false and true or false:
      echo "long condition lines should be indented"
    elif true or false and true or false
         true or false and true or false:
      echo "long condition lines should be indented"
    else:
      echo "foo"
    

    上記のような場合、カーソルのあるポイントから後ろに戻って if を探して etc.とかやってたのが、

    (defconst nim-mode-smie-grammar
    (smie-prec2->grammar
     (smie-merge-prec2s
      (smie-bnf->prec2
       '((id)
         (stmts (stmt ";" stmt) (stmts))
         (stmt (exp) (exp))
         (exp (id) (exp) (exp "=" exp))
         (conditions
          ("if" exp "elif" exp "else" ":")))
       ;; 演算子の順位付けのための設定
       ;; 以下は if/when/case がネストした場合でも
       ;; 最短の親要素を探すために必要でした。(elif や else で
       ;; インデント計算するとき)
       '((nonassoc "if" "when" "case")
         (assoc "of") (assoc "elif") (assoc "else")))))
    

    のように書くだけだったりと劇的に簡単にインデントを計算ができるようになります。(細かいインデント調整は必要になりますが、、、))

  • 例 2: 同じ予約語を使いまわしている場合でも…

    proc complex() =
      case condition:
        of "with colon":
          if true:
            echo "foo"
          else: echo "one line else"
        of "second OF":
          echo "hello"
        of "one line OF": echo "hello"
        else:
          echo "case done"
          when true:
            echo "hello"
          elif true:
            echo "make sure 'elif' is working"
          else: echo "one line else"
          if true:
            echo "after ELSE ended with single line"
    

    nim では if 文 when 文とも elif と else を使い、さらに case 文でも elif と else を使うという実際実装するのが非常にめんどくさそうな構文を持っています。(python.el からかなり パクって 再利用していたので古い nim-mode でも特に苦労はなかったですが、) このような場合、問題になる elif と else がどこに属しているかを探すのが結構めんどうでした。

    それが smie を使うとパースしたときに特定のインデント設定を書くのが非常に楽です。

    どう楽かというと smie では XXX-smie-rules というような関数を各言語ように作り、トークンに対してインデント計算を補助できます。

    上記の例でいうと:

      (defun nim-mode-smie-rules (kind token)
      "Nim-mode ’ s indent rules.
    See also ‘ smie-rules-function ’ about KIND and TOKEN."
      (pcase (cons kind token)
         (`(:after . "else")
          (if-let ((name (smie-rule-parent-p "if" "when" "case")))
             ...))))
    

    のような感じで親が何かで簡単に分岐できます。

    …実際には上記の例では smie-rule-parent-p は名前しか返してくれないので、`smie-indent–parent`関数を使いました。(親のポイントと名前を返してくれます。)

    ただ名前に–が入っているしもしかしたら使わないほうがよいかも… XXX-rules 関数の中で使う文には問題はなかったです。

    余談ですが、nim の case 文は case のあとにコロンはオプショナルで付けば1段階インデントを深くします。(なければ case と同じインデントが of, elif,else で続く)

実装するときの注意とか

  • BNF っぽい書き方をする smie の grammar 変数ですが、なれるまで結構苦労するので、一個ずつテストを作ったほうがいいかもしれません。一つの変更が広範囲に影響するので、、
  • 上記の grammar 変数ですが、いい感じに書くと親と子の要素を highlight してくれるので成功しているかのだいたいの確認になります。 (正確にコード読んでないですが、ハイライトが成功しているなら smie-rules-parent 関数とかが正常に機能していました。)
  • このハイライトですが、グラマーが複雑になってくると動かなくなる場合があるので(カーソルが動かなくなる)、以下のようにデフォルトの show-paren の関数を set するのもいいかもしれません。

    (setq-local show-paren-data-function #'show-paren--default)
    
  • smie.el は作者が Emacs のメンテナーの Stefan Monnier さんなので、edebug に対応してたりとなかなかソースみるのが面白い は edebug に対応しているので M-x smie-edebug で SPC でステップ実行できるので emacs 初心者にも結構やさしいです。

    自分は message buffer にでれば十分だったので以下のような advice を書いて edebug は使いませんでしたが、

    (defadvice nim-mode-smie-rules (around output-debug-message activate)
      "Output debug information."
      (message (format "Point: %d" (point)))
      (message (format "- IN - Kind: ’%s ’ Token: ’%s ’"
                       (ad-get-arg 0) (ad-get-arg 1)))
      ad-do-it
      (message (format "- OUT - ’%s ’" ad-return-value)))
    
  • キータの記事でも書かれてましたが、smie.el を使って書かれている ruby-mode, elixir-mode や tuareg-mode を見てみるのが参考になりました。 python っぽい文法であれば、nim-mode が参考になるかも…まだ足りない部分かなりあるとおもいますが、
  • xxx-smie-rules 関数に関する memo
    • kind が:list-intro の時は数値でなく t or nil を返す
    • :elem - empty-line-token の時は token を返す (実装が black magic 的って作者が comment に書いてる…)

おわり

smie.el メジャーモードのインデント計算では非常に便利だったので、新たに言語のメジャーモードを書く方がいましたら、ぜひ使ってみてください。

2016年1月5日火曜日

Blogger に post するコマンドラインツールを作った

このブログの更新には googlecl というコマンドラインツール経由で emacs から記事を書いていたけれど、結構前から googlecl が oauth2 に対応してなくてブログが更新できなかった。自分がなんか作った途端マーフィーの法則でもっといいツールが出たりすぐ治るのではという恐れがあってずっと googlecl の github Issues が更新されるのを待つ日々が続いたが、更新されなそうなので、シンプルに google blogger にポストするだけのコマンドラインツールを作った。実装には勉強ついでに nimlang を使ってみた。 (自分用に Emacs から post するコードも入っているので興味がある人は後半の方もみてみてください。)

前準備

nim の実行環境がない人は、先に ここから nim をインストールしてください。あと、 nimble という nim 用のパッケージマネージャもあったほうが管理が楽です。

あと Google の Blogger 用の API 利用するのに Google Developper Console で登録する必要があります。 もはや記憶の彼方でどうやったかあまり思い出せないのですが、多分ググればいろいろ情報があるのでは、、、(適当)

とりあえず、client id と client secret というのが必要です。

インストール

準備ができたら、

git clone https://github.com/yuutayamada/nim-blogger
cd CLONED DIRECTORY
nimble make

で nimblogger という実行ファイルが作れます。

ホントは nimble install でインストールできるようにしたかったけど、-d:ssl というフラグをつけないとビルドできず install のときどうやって指定するのかよくわからなかったので、このようになってしまいました。

# たぶん Google Developper Console の API manager から
# みつけられるとおもいます
export GOOGLE_API_CLIENT_ID="developper console から client_id をコピペしてください"
export GOOGLE_API_CLIENT_SECRET="developper console から client_secret をコピペしてください"

のあと

nimblogger --help

で使い方がでるとおもいます。 (注意 Refresh token なんちゃらってでたらもう一回やると成功するかもしれません。)

Emacs から

自分用なので使いたい人は使ってというスタンスで、misc ディレクトリに org-mode のドキュメントから html に変換してポストするコードが入ってます。

使いたい人は、下のような設定をしたあと`nimblogger:post-article`関数で投稿できるとおもいます。

(load "path/to/misc/nimblogger")
(setq nimblogger:blog-name "blog の名前"
      ;; binary file へのパス
      nimblogger:command "path/to/nim-blogger/nimblogger")
(define-key org-mode-map (kbd "C-S-p") 'nimblogger:post-article)

おわりに

自分が必要な最低限はできたけど、Google の更新についてく自信がないいので、いいツールがあれば教えてください。

Popular Posts

Blogger templates

Blogger news