erlterm: Erlang項とCommon Lispオブジェクトの相互変換

Erlang -- External Term Formatを参考にして、Erlangの項(のバイナリ表現)Common Lispのオブジェクトを相互変換するライブラリを作成。
erlterm-0.0.1
ポートを用いた外部接続での利用を想定。
細かい作り込みや最適化はまだまだだけど、一応一通りの変換は行える。※ 未対応箇所に関しては、erltermのREADMEを参照

使用例

軽く使用例を。
使える関数はerlterm:decode-termとerlterm:encode-termの二つ。

;;;; Common Lisp側# Erlangから送られてきたリストを反転して送り返す
;;;; ファイル名: reverse.lisp
;;;;
;;;; 処理系: sbcl-1.0.40  
;;;;         ※ 標準入出力に対してバイナリ操作が行えるのは(おそらく)SBCLの拡張
(require :asdf)
(require :erlterm)

(handler-case
  (loop 
    (let ((list (erlterm:decode-term *standard-input* :packet 2)))     ; 受信
      (erlterm:encode-term (reverse list) *standard-output* :packet 2) ; 返信
      (force-output *out*)))
  (end-of-file ()
    'done))
%%%% Erlang側# ポート関連の操作をラップ
%%%% ファイル名: port_rpc.erl
-module(port_rpc).
-export([open/1,close/1,call/2]).

%% 接続
open(Command) ->
    open_port({spawn, Command}, [{packet, 2},binary]).
    
%% クローズ
close(Port) ->
    Port ! {self(), close}.

%% リクエスト送信
call(Port, Message) ->
    Port ! {self(), {command, term_to_binary(Message)}},  % バイナリに変換して送信
    receive 
        {Port, {data, Data}} -> binary_to_term(Data)      % レスポンスをバイナリから復元
    end.
%%%% Erlangシェル
%% コンパイル & ロード 
> c(port_rpc).
 => {ok,port_rpc}

%% lispプロセスを開く
> Port = port_rpc:open("sbcl --script reverse.lisp").
 => #Port<0.1723>

%% 送信するデータ
> List = [first, {self(), {1, "middle", 3.2}, make_ref()}, last].
 => [first,{<0.33.0>,{1,"middle",3.2},#Ref<0.0.0.66>},last]

%% リクエスト
> port_rpc:call(Port, List).
 => [last,{<0.33.0>,{1,"middle",3.2},#Ref<0.0.0.66>},first]

%% クローズ
> port_rpc:close(Port).
 => {<0.33.0>,close}

もう一つ。
Erlang項のデコード例。

%%%% Erlang
%% 上の例のリストをバイナリに変換して、ファイルに書き出す
> file:write_file("list.bin", term_to_binary([first, {self(), {1, "middle", 3.2}, make_ref()}, last])).

%% 無名関数をバイナリに変換して、ファイルに書き出す
> file:write_file("fun.bin", term_to_binary(fun (X) -> X*X*X end)).
;;;; Common Lisp
(require :erlterm)

;; Erlangのリストのバイナリデータを読み込む
(with-open-file (in "list.bin" :element-type '(unsigned-byte 8))
  (erlterm:decode-term in))
-->
(:FIRST
 #(#S(ERLTERM::PID :NODE :NONODE@NOHOST :ID 33 :SERIAL 0 :CREATION 0)
   #(1 "middle" 3.2d0)
   #S(ERLTERM::NEW-REFERENCE :NODE :NONODE@NOHOST :CREATION 0 :ID #(94 0 0)))
 :LAST)

;; Erlang無名関数のバイナリデータを読み込む
(with-open-file (in "fun.bin" :element-type '(unsigned-byte 8))
  (erlterm:decode-term in))
-->
#S(ERLTERM::NEW-FUN
   :SIZE 722
   :ARITY 1
   :UNIQ 75230672122209326657346363854110770891
   :INDEX 2
   :MODULE :ERL_EVAL
   :OLD-INDEX 6
   :OLD-UNIQ 13229925
   :PID #S(ERLTERM::PID :NODE :NONODE@NOHOST :ID 49 :SERIAL 0 :CREATION 0)
   :FREE-VARS #(NIL
                #(:VALUE
                  #S(ERLTERM::NEW-FUN
                     :SIZE 96
                     :ARITY 2
                     :UNIQ 9719462018325469980632351212009778472
                     :INDEX 7
                     :MODULE :SHELL
                     :OLD-INDEX 7
                     :OLD-UNIQ 115410035
                     :PID #S(ERLTERM::PID
                             :NODE :NONODE@NOHOST
                             :ID 49
                             :SERIAL 0
                             :CREATION 0)
                     :FREE-VARS #(#S(ERLTERM::PID
                                     :NODE :NONODE@NOHOST
                                     :ID 27
                                     :SERIAL 0
                                     :CREATION 0))))
                #(:EVAL
                  #S(ERLTERM::NEW-FUN
                     :SIZE 417
                     :ARITY 3
                     :UNIQ 9719462018325469980632351212009778472
                     :INDEX 14
                     :MODULE :SHELL
                     :OLD-INDEX 24
                     :OLD-UNIQ 81953817
                     :PID #S(ERLTERM::PID
                             :NODE :NONODE@NOHOST
                             :ID 49
                             :SERIAL 0
                             :CREATION 0)
                     :FREE-VARS #(#(:VALUE
                                    #S(ERLTERM::NEW-FUN
                                       :SIZE 96
                                       :ARITY 2
                                       :UNIQ 9719462018325469980632351212009778472
                                       :INDEX 7
                                       :MODULE :SHELL
                                       :OLD-INDEX 7
                                       :OLD-UNIQ 115410035
                                       :PID #S(ERLTERM::PID
                                               :NODE :NONODE@NOHOST
                                               :ID 49
                                               :SERIAL 0
                                               :CREATION 0)
                                       :FREE-VARS #(#S(ERLTERM::PID
                                                       :NODE :NONODE@NOHOST
                                                       :ID 27
                                                       :SERIAL 0
                                                       :CREATION 0))))
                                  8207
                                  #S(ERLTERM::NEW-FUN
                                     :SIZE 208
                                     :ARITY 1
                                     :UNIQ 9719462018325469980632351212009778472
                                     :INDEX 15
                                     :MODULE :SHELL
                                     :OLD-INDEX 14
                                     :OLD-UNIQ 68813366
                                     :PID #S(ERLTERM::PID
                                             :NODE :NONODE@NOHOST
                                             :ID 49
                                             :SERIAL 0
                                             :CREATION 0)
                                     :FREE-VARS #(#(:VALUE
                                                    #S(ERLTERM::NEW-FUN
                                                       :SIZE 96
                                                       :ARITY 2
                                                       :UNIQ 9719462018325469980632351212009778472
                                                       :INDEX 7
                                                       :MODULE :SHELL
                                                       :OLD-INDEX 7
                                                       :OLD-UNIQ 115410035
                                                       :PID #S(ERLTERM::PID
                                                               :NODE :NONODE@NOHOST
                                                               :ID 49
                                                               :SERIAL 0
                                                               :CREATION 0)
                                                       :FREE-VARS #(#S(ERLTERM::PID
                                                                       :NODE :NONODE@NOHOST
                                                                       :ID 27
                                                                       :SERIAL 0
                                                                       :CREATION 0))))
                                                  8207
                                                  #S(ERLTERM::PID
                                                     :NODE :NONODE@NOHOST
                                                     :ID 27
                                                     :SERIAL 0
                                                     :CREATION 0)))
                                  #S(ERLTERM::PID
                                     :NODE :NONODE@NOHOST
                                     :ID 27
                                     :SERIAL 0
                                     :CREATION 0))))
                ;; (X) -> X * X * X
                (#(:CLAUSE 1 (#(:VAR 1 :|x|)) NIL
                   (#(:OP 1 :* #(:OP 1 :* #(:VAR 1 :|x|) #(:VAR 1 :|x|))
                      #(:VAR 1 :|x|)))))))