LISPUSER

ECL – Embeddable Common-LispLisp isn't a language, it's a building material.

(Top Page) (Lisp Memo)

公式ページhttp://ecls.sourceforge.net/
最新バージョン0.9.6.2

翻訳情報

ECL について

ECL って何?

ECL は埋め込み可能な Common Lisp です。 ECL プロジェクトは Giuseppe Attardi の ECL 環境を、ANSI X3J13 言語規定に準拠する実装へと近代化したものです。 現在の ECL 実装の特徴は以下のような特徴を持ちます:

  • バイトコードコンパイラとインタプリタ
  • C へのトランスレータ
  • UFFI コンパチブルな C コードへのインターフェース
  • ダイナミックローダー
  • スタンドアロン実行形式や DLL をビルドすることが可能
  • The Common-Lisp Object System (CLOS).
  • コンディションやエラーハンドリングのためのリスタート
  • 普通のストリームと同じように扱える Socket
  • 高速な多倍長精度演算のために Gnu Multiprecision ライブラリを利用します
  • 単純な保守的マーク&スィープガベージコレクタ
  • Boehm-Weiser ガベージコレクタ

ECL は Intel, Appeal, Alpha, PowerPC プロセッサ上で動作するLinux, FreeBSD, NetBSD, Solaris, Windows といった OS をサポートします。他のアーキテクチャへの移植も簡単です。

作者

オリジナルの版は Giuseppe Attardi によって書かれました。 現在の ECL のメンテナは Juan Jose Garcia Ripoll です。 ECL のメーリングリストを使って連絡を取ることができます。 我々はテストや実装を改良してくれるボランティアを求めています。 コードや建設的な批判を歓迎します。 気軽にメーリングリストに参加して、あなたの考えを語ってください。

日本語情報

インストール

tar xzf ecl-0.9i.tar.gz
./configure  --srcdir=./src --prefix=$HOME/local --enable-threads=yes --with-__thread
make
make install

こんにちは ECL

% ecl
ECL (Embeddable Common-Lisp) 0.9i
Copyright (C) 1984 Taiichi Yuasa and Masami Hagiya
Copyright (C) 1993 Giuseppe Attardi
Copyright (C) 2000 Juan J. Garcia-Ripoll
ECL is free software, and you are welcome to redistribute it
under certain conditions; see file 'Copyright' for details.
Type :h for Help.  Top level.
> (defun hello () (write-line "hello"))
HELLO

スタンドアロンバイナリを生成する

まずスタンドアロンバイナリにしたい Lisp コードを用意する。

% cat >hello.lisp
(defun hello ()
  (write-line "Hello, World!!"))

(hello)
(quit 0)

次に ecl を起動して、先程のソースを compile-file する。 この時、compile-file にキーワード引数 :system-p t を渡そう。 すると、 hello.lisp から hello.o というオブジェクトファイルが生成される。

そして、 c:build-program 関数を使って、さきほどのオブジェクトファイルから スタンドアロンバイナリを生成する。

colinux% ecl
ECL (Embeddable Common-Lisp) 0.9i
Copyright (C) 1984 Taiichi Yuasa and Masami Hagiya
Copyright (C) 1993 Giuseppe Attardi
Copyright (C) 2000 Juan J. Garcia-Ripoll
ECL is free software, and you are welcome to redistribute it
under certain conditions; see file 'Copyright' for details.
Type :h for Help.  Top level.
> (compile-file "hello.lisp" :system-p t)
;;; Loading #P"/home/onjo/local/lib/ecl/cmp.fas"
;;; Loading #P"/home/onjo/local/lib/ecl/sysfun.lsp"
;;; Compiling hello.lisp.
;;; Compiling (DEFUN HELLO ...).
;;; End of Pass 1.
;;; Emitting code for HELLO.
;;; Calling the C compiler...
;;; Note: Invoking external command:
;;; g++  -D_GNU_SOURCE -g -O2 -fPIC  -fstrict-aliasing -Dlinux -O
"-I/home/onjo/local/include/" -w -c "/home/onjo/project/myecl/hello.c"
-o "/home/onjo/project/myecl/hello.o"

;;; OPTIMIZE levels: Safety=2, Space=0, Speed=3
;;; Finished compiling hello.lisp.
#P"/home/onjo/project/myecl/hello.o"
NIL
NIL
> (c:build-program "a.out" :lisp-files '("hello.o"))
;;; Warning: File #P"a.out" is of no known file type. Assuming it is
;;; an object file.
;;; Note: Invoking external command:
;;; g++  -D_GNU_SOURCE -g -O2 -fPIC  -fstrict-aliasing -Dlinux -O
;;; "-I/home/onjo/local/include/" -w -c
;;; "/home/onjo/project/myecl/ECLINITPVvuq7.c" -o
;;; "/home/onjo/project/myecl/ECLINITPVvuq7.o"

;;; Note: Invoking external command:
;;; g++ -o "/home/onjo/project/myecl/a.out" -L"/home/onjo/local/lib/"
;;; "/home/onjo/project/myecl/ECLINITPVvuq7.o"
;;; "/home/onjo/project/myecl/hello.o"  -Wl,--rpath,/usr/lib/ecl/
;;; -lecl -ldl  -lm   -lgmp

#P"a.out"
>

できあがり!! 完成した a.out を実行して出力を確認してみよう。

% ./a.out
Hello, World!!

ECL を C のプログラムに埋め込む

ECL で Lisp ソースをコンパイルした FASL ファイルをロードして、その中の MAIN 関数を 呼び出す C のプログラムを作成してみる。 FASL ファイルが指定されなかったら REPL を起動することにする。

% cat >myecl.c
#include <stdio.h>
#include <dlfcn.h>
#include "ecl/ecl.h"

static cl_object si_simple_toplevel ()
{
        cl_object sentence;
        int i;

        /* Simple minded top level loop */
        for (i = 1; i<fix(si_argc()); i++) {
          cl_object arg = si_argv(MAKE_FIXNUM(i));
          cl_load(1, arg);
        }
        while (1) {
          printf("\n> ");
          sentence = cl_read(3, Cnil, Cnil, OBJNULL);
          if (sentence == OBJNULL)
            return Cnil;
          ecl_prin1(si_eval_with_env(1, sentence), Cnil);
        }
        return Ct;
}

int main (int argc, char *argv[])
{
  cl_object func = NULL;

  cl_boot(argc, argv);
  si_trap_fpe(Ct, Cnil);

  if (fix(si_argc()) != 1) {
    cl_load(1, si_argv(MAKE_FIXNUM(1)));
    func = _ecl_intern("MAIN", cl_core.user_package);
    funcall(1, func);
  } else {
    cl_object toplevel;
    printf("[usage] myecl plugin.fas\n\n");
    printf("fallback to sompile top level ...\n");
    toplevel = _ecl_intern("TOP-LEVEL", cl_core.system_package);
    cl_def_c_function(toplevel, si_simple_toplevel, 0);
    funcall(1, toplevel);
  }

  cl_shutdown();

  return 0;
}
% gcc -Wall -o myecl myecl.c `ecl-config --cflags` `ecl-config --libs`

次に、この myecl にロードする Lisp プログラムを用意する。

% cat >hello.lisp
(defun message-loop (msg)
  (loop for i from 0 upto 5000 do
        (if (= (random 10) 0)
            (sleep 0)
               (write-string msg))))

(defun main ()
   (mp:process-run-function "Test A" #'message-loop "A")
   (mp:process-run-function "Test B" #'message-loop "B")
   (mp:process-run-function "Test C" #'message-loop "C")
   (mp:process-run-function "Test D" #'message-loop "D")
   (mp:process-run-function "Test E" #'message-loop "E")
   (sleep 5)
   (terpri)
   (quit 0))

A, B, C, D, E の各文字をひたすら出力するスレッドを 5 本動作させる。 スレッドのスイッチに応じて AAABBCCDDCCBB… のような出力がえられるはず。

% ecl -compile hello.lisp
;;; Loading #P"/home/onjo/local/lib/ecl/cmp.fas"
;;; Loading #P"/home/onjo/local/lib/ecl/sysfun.lsp"
;;; Compiling hello.lisp.
;;; Compiling (DEFUN HELLO ...).
;;; Compiling (DEFUN MESSAGE-LOOP ...).
;;; Compiling (DEFUN MAIN ...).
;;; End of Pass 1.
;;; Emitting code for HELLO.
;;; Emitting code for MESSAGE-LOOP.
;;; Emitting code for MAIN.
;;; Calling the C compiler...
;;; Note: Invoking external command:
;;; g++  -D_GNU_SOURCE -g -O2 -fPIC  -fstrict-aliasing -Dlinux -O
"-I/home/onjo/local/include/" -w -c "/home/onjo/project/myecl/hello.c"
-o "/home/onjo/project/myecl/hello.o"

;;; Note: Invoking external command:
;;; g++ -o "/home/onjo/project/myecl/hello.fas"
;;; -L"/home/onjo/local/lib/" "/home/onjo/project/myecl/hello.o"
;;; -Wl,--rpath,/usr/lib/ecl/ -shared    -lecl -ldl  -lm   -lgmp

;;; OPTIMIZE levels: Safety=2, Space=0, Speed=3
;;; Finished compiling hello.lisp.

コンパイルされた Lisp コードを myecl の引数に与えると、main が起動される。

% myecl hello.fas
;;; Loading "/home/onjo/project/myecl/hello.fas"

AAAAAAAAAAAAAAABBBBBBBBBBCCCCCCCDDDDDDAABBBBBBBBCCCCCCCCCCCCCCCDDEEEEEEE
EEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBCCCABBBBBBBBBBB
BBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCAAAAAAAADDDDDDDDDDDDDDDDDDDDDDD
...

これは、Lisp 側でスレッドを使用しているが、C 側でスレッドを生成して、そこで ECL インタプリタを使う場合もあるだろう(というか、そっちが多いか?)。 そういったケースのサンプルは、ECL のソースツリーの examples フォルダにある。

static void *
thread_entry_point(void *data)
{
        cl_object form;

        ecl_import_current_thread(Cnil, Cnil); // 初期化

        cl_eval(form);

        ecl_release_current_thread(); // 解放

        return NULL;
}


int main(int narg, char **argv)
{
        pthread_t child_thread;
        int i, code;

        cl_boot(narg, argv);

        cl_object sym_print = c_string_to_object("PRINT");
        for (i = 0; i < 10; i++) {
                cl_object form = cl_list(2, sym_print, MAKE_FIXNUM(i));
                code = pthread_create(&child_thread, NULL, thread_entry_point,
                                      (void*)form);
                if (code) {
                        printf("Unable to create thread\n");
                        exit(1);
                }
        }

        pthread_join(child_thread, NULL);

        return 0;
}

ecl_import_current_thread, ecl_release_current_thread がキモ。上記のものは コメントを削除しているので、詳細な説明は本当のソースを参照してください。

TODO: C 側 API の説明とかがないとなぁ…

$Last Update: 2007/09/09 21:18:40 $