apropos

前回の最後にやったgauche用のslimeなんだけど、指示に従ってsrcエリアの情報を参照するように設定すると、不可解なエラーに落ちる。で、そのコードを抜き出してみた。まずは実行結果。

gosh> ,l ./aa.scm
*** READ-ERROR: Read error at "(input string port)":line 1: extra close parenthesis `]'
    While loading "./aa.scm" at line 23
Stack Trace:
_______________________________________
  0  (proc l)
        at "./aa.scm":9
  1  (load-operator-args gauche-refe-path)
        at "./aa.scm":23

実験に使った、思いっきりの簡略版。

(define (grep regex-str file proc)
  (let ((regex (string->regexp regex-str)))
    (with-input-from-file file
      (lambda ()
        (let loop ((acc '()))
          (let ((l (read-line)))
            (cond ((eof-object? l) (reverse acc))
                  (else (if (regex l)
                            (loop (cons (proc l) acc))
                            (loop acc))))))))))
(define gauche-refe-path "/home/sakae/src/Gauche-0.9.8/doc/gauche-refe.texi")
(define (load-operator-args gauche-refe-path)
  (grep "^@defun|^@defmac|^@defspec|^@deffn"  gauche-refe-path
        (lambda (line)
          (read-from-string
           (regexp-replace-all* #`"(,line)"
                                #/@dots{}/ "..."
                                #/@code{(\S*)}/ "\\1"
                                #/@def\w+ / ""
                                #/:optional/ "&optional"
                                #/\[(.*)\]/ "&optional \\1")))))

(define ZZZ (load-operator-args gauche-refe-path))

docの下に有るinfoの原稿から、冒頭が@defun等で始まる行を抜き出して、その行内を改変。 そして、read-from-stringで、変更済の文字列をリストにしてる。

その為の仕掛けがregex-replase-allに並置されてる所だ。マッチした文字列の前後を括弧に囲ってる。上手い方法だな。最初は理解出来なかったけどね。

grepの本体では、加工されてリストになったものをどんどんとconsしてる。

どうも出来上がった結果に問題がありそうなんだけど、目で確認したい。guardとか言うのを使うと、その瞬間を捉えられるそうなんだけど、オーソドックスな方法を取った。 read-from-stringを外しておいて、文字列をcons。その結果をファイルに落とした。

gosh> (define oo (open-output-file "ZZZ"))
oo
gosh> (pprint ZZZ :port oo)
#<undef>
gosh> (close-port oo)

文字列に]は現れないはずなんだけど、検索すると、

(base) [sakae@c8 tmp]$ grep -- ] ZZZ
 "(dotimes (&optional variable] num-expr [result) body ...)"
 "(dolist (&optional variable] list-expr [result) body ...)"
   :

そして原稿の方

(base) [sakae@c8 doc]$ grep dotimes gauche-refe.texi
@defmac dotimes ([variable] num-expr [result]) body @dots{}

正規表現ってのは、難しいな。誰だこんな便利なやつを発明したのは? 際限なく拡張するperlは趣味人の集まりだなあ。ハッカーの巣窟はBugの巣窟になりますよ。

r7rsの怪

前回からの続き。普通のgoshを起動した積りが、途中からr7rsモードに移行しちゃってて、不思議だなあと思ってた。

(defun geiser-gauche--parameters ()
  `("-I" ,(expand-file-name "gauche/geiser" geiser-scheme-dir) "-i" "-l" "gosh.scm"))

geiser-gauche.el/geiser-gauche--startupの一節を真似てみる。

(base) [sakae@c8 geiser]$ gosh -I . -l gosh.scm -i
gosh> (begin (import (geiser)) (write `((result ) (output . ""))) (newline))
*** ERROR: proper list required for function application or macro use: (output . "")
    While compiling "(standard input)" at line 1: (begin (import (geiser)) (write `((result) (output . ""))) (newline))
Stack Trace:
_______________________________________
  0  (eval expr env)
        at "/usr/local/share/gauche-0.97/0.9.8/lib/gauche/interactive.scm":269
gosh[r7rs.user]>

不思議発見である。冒頭に(import (xxx))ってのが来ると、goshはr7rs形式で書かれているものと思って、-r7 モードに切り替えてしまう。以後ずっとrsr7モードとして動作するとな。

apropos

geiserのchez側のcodeを見ていると、何やらmoduleって語句が頻出してる。これはきっと補完のデータとか引数の情報を抽出してるに違いない。先回りしてgaucheのやつを調べておくか。関数名ならaproposだな。ソースに当たる。lib/gauche/interactive.scm

;;; Apropos - search bound symbols matching given pattern
;;;
;;;  (apropos 'open)             print bound symbols that contains "open"
;;;                              in its name
;;;  (apropos #/^(open|close)/)  you can use regexp
;;;
;;;  (apropos 'open 'scheme)     search symbols only in a single module

(define-syntax apropos
  (syntax-rules ()
    [(_ item) (%apropos item (current-module) #f)]
    [(_ item module) (%apropos item module #t)]
    ))

引数の与え方に2つのパターンが有るとな。そのパターンをマクロで処理してる。下請けは%付きのaproposか。期待してた、結果をリストにするような機能は付いていない。まあ、人間相手のマクロだからしょうがないか。どうしてもリスト化したいなら、改造しろとな。

改造は最後の手段として、結果をファイルに落としておきたい。どうする?

(let ((keep (current-output-port)))
  (current-output-port (open-output-file "ZZZ"))
  (apropos #/.*/)
  (current-output-port keep))

一時的にファイルポートを現在のポートに挿げ替えて、aproposさせる。終了したらポートを元に戻しておく。

(with-output-to-file "KEY" (^() (apropos #/.*/ 'keyword)))

もっと手軽にやるなら、上記の方法でも良い。^ は、lambdaの代わりね。

(base) sakae@debian:tmp$ cut -d'(' -f2 ZZZ | sort | uniq -c | sort -nr
   1619 gauche)
    290 keyword)
    194 scheme)
     30 gauche.interactive)
     26 null)
      4 user)

こうやって取得した結果をモジュール名でヒストグラム化してみた。6種類のモジュールが初期のrepl環境で有効になってるのね。他のモジュールも利用するなら、先にuseしておけばよいな。

repl上で定義したシンボルは、userモジュール内に格納されるんだな。

gosh> (define (dump module-name)
        (hash-table-keys (module-table (find-module module-name))))
dump
gosh> (define hoge-fuga 123)
hoge-fuga
gosh> (find-module 'user)
#<module user>
gosh> (module-table (find-module 'user))
#<hash-table eq? 0x7f6909062b40>
gosh> (dump 'user)
(*program-name* import hoge-fuga dump *argv* help)

hashならキーに対してバリューもあるはず。

gosh> (hash-table-values (module-table (find-module 'user)))
(#<gloc user#*program-name*> #<gloc user#import> #<gloc user#hoge-fuga>
 #<gloc user#dump> #<gloc user#*argv*> #<gloc user#help>)

これを使うのは評価器ですとな。

gosh> (apropos 'select-module)
select-module                  (gauche)
gosh> (define-in-module scheme select-module select-module)
select-module
gosh> (apropos 'select-module)
select-module                  (gauche)
select-module                  (scheme)

module間を移動するのに、select-moduleを使う。だが、こやつはgaucheなモジュールにしか入っていない。userモジュールからはgaucheが見えるんでselect-moduleが使える。

だが、間違ってschemeモジュールの中に入ってしまうと、脱出手段が無くなる。そんな事もあろーかと、schemeモジュールの中に、select-moduleを忍ばせておく。

info lookup for gauche

emacsからinfoを引く設定 by shiroさん。

(defun gauche-info-index (topic)
  (interactive
   (list (read-string
          (concat "Gauche help topic : ")
          (current-word))))
  (switch-to-buffer-other-window (get-buffer-create "*info*"))
  (info "/usr/local/share/info/gauche-refe.info.gz")
  (Info-index topic))
(define-key global-map "\C-x\C-j" 'gauche-info-index)

rlwrap + gosh

普通のgoshを使い易くする設定。

VimでのSchemeプログラミング

gauche+rlwrapで関数補完を行う

改めてGaucheとrlwrapの連携について

gaucheに入力補完機能を追加

what is company

geiserにはcompanyって言う補完機能が使われていたので、少し資料収集。

emacsの補完用パッケージcompany-mode

company-mode support through third-party backends

自作言語 (LuneScript) の emacs company-mode backend 設定

車輪の再発明? かな。

Emacs で Language Server Protocol を使ってみる

好みのエディタに快適な開発環境を提供するLSP

「Emacsのトラノマキ」連載第09回「auto-completeを使おう」(松山智大)

auto-complete

取り合えずschemeでも補完って事で、auto-completeを入れておく。

;; auto-complete using dict
(require 'auto-complete-config)
(ac-config-default)
(setq ac-use-menu-map t)  ;; ctl-n/p
(add-to-list 'ac-dictionary-directories
             "~/.emacs.d/elpa/auto-complete-20170125.245/dict")
(set-face-background 'ac-selection-face "blue")
(set-face-foreground 'ac-completion-face "red")
(set-face-background 'ac-completion-face "gray")

dictの中には色々な言語の辞書が格納されている。勿論scheme用も備え付け。だがこの辞書は貧弱なので、rlwrapの奴を利用しよう。単にリンクを張るだけって言うインチキな方法でね。

[sakae@fb dict]$ mv scheme-mode scheme-mode.org
[sakae@fb dict]$ ln -s ~/.gosh_completions scheme-mode

そしてgoshの起動用。

;; scheme
(setq scheme-program-name "gosh -i")
(autoload 'scheme-mode "cmuscheme" "Major mode for Scheme." t)
(autoload 'run-scheme "cmuscheme" "Run an inferior Scheme process." t)
(defun scheme-other-window ()
  (interactive)
  (switch-to-buffer-other-window
   (get-buffer-create "*scheme*"))
  (run-scheme scheme-program-name))
(define-key global-map   "\C-cC-z" 'scheme-other-window)

これで rlwrpa+gosh の環境が、emacs上に実現出来た。emacsで動いているって事はeditor主体で、時々replって事ね。

[sakae@fb /tmp]$ export EDITOR=emacs
[sakae@fb /tmp]$ gosh
gosh> (ed "aa.scm")
Reload "aa.scm"? [y/N]: y

まあ、replから好きなeditorを呼び出せるから、emacsに拘る必要は無いんだけどね。詳細は、,doc edとかすれば出てくる。

etc

https://b.hatena.ne.jp/search/tag?q=gauche

暫くはクリスマスのクッキー代わりに食べていよう。 Advent Calendar 2019 は、来年のお楽しみ!

よくわかるcall/cc

chezのコードを見ていたら、call/ccが使われていたので、継続は力也なんでしょうな。