英語原文: http://ecls.sourceforge.net/
ECL は埋め込み可能な Common Lisp です。 ECL プロジェクトは Giuseppe Attardi の ECL 環境を、ANSI X3J13 言語規定に準拠する実装へと近代化したものです。 現在の ECL 実装の特徴は以下のような特徴を持ちます:
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 (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 で 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 $