GaucheのScheme処理系APIをC言語から利用する記述のサンプル

【2020-08-19追記】[Python3版の記事](https://qiita.com/ytaki0801/items/9d30c65e5edba7dcb0fe)を作成しました．
【2020-08-18追記】[GNU Guile版の記事](https://qiita.com/ytaki0801/items/83f8bc510342b7945519)を作成しました．

Schemeは，そのコンパクトな言語仕様から，機能拡張用言語としてアプリケーションソフトウェアに処理系自体が組み込まれることがある．そのような仕組みは，[GNU Emacs](https://www.gnu.org/software/emacs/)の[Emacs Lisp](https://www.gnu.org/software/emacs/manual/html_node/eintr/Preface.html#Preface)が有名だが，Schemeを主言語とする[GNU Guile](https://www.gnu.org/software/guile/)が組込用を想定して開発されており，また，[Julia](https://docs.julialang.org/en/v1/)処理系には独自開発の小さなSchemeインタプリタが組み込まれて[フロントエンド（パーサ）に](https://docs.julialang.org/en/v1/devdocs/ast/)用いられている．

この記事では，同じく[組み込みSchemeライブラリとしての利用](https://practical-scheme.net/gauche/man/gauche-refj/GauchewoQi-Dong-suru.html#Gauche_3092_8d77_52d5_3059_308b)も想定されている[Gauche](https://practical-scheme.net/gauche/index-j.html)について，Scheme処理系APIをC言語から利用する，ごく簡単な記述サンプルをまとめている．動作確認環境は次の通り．

* Raspberry Pi 4 Model B (Raspberry Pi OSプリインストール)
* gcc (Raspbian 8.3.0-6+rpi1) 8.3.0
* Gauche 0.9.9（ソースコードからインストール）

#C言語プログラムでScheme記述を文字列として渡して評価結果を表示する例

```C:gauche-fib.c
#include <stdio.h>
#include <gauche.h>

#define FIB "(let ((n (read))) (map (lambda (x) (let f ((i x) (f1 0) (f2 1)) (if (= i 0) f1 (f (- i 1) f2 (+ f1 f2))))) n))"

int main(int argc, char **argv)
{
  GC_INIT(); 
  Scm_Init(GAUCHE_SIGNATURE);

  ScmEvalPacket eRet;
  if (Scm_EvalCString(FIB, SCM_NIL, &eRet) < 0) {
    printf("Error in S expression\n"); return (1);
  }
  Scm_Printf(SCM_CUROUT, "%S\n", eRet.results[0]);

  Scm_Exit(0); 

  return(0);
}
```

コンパイルおよび実行例は次の通り．

```bash
$ cc -Wall `gauche-config -I` -o gauche-fib gauche-fib.c `gauche-config -l`
$ ./gauche-fib
(0 10 20 30 40)                 # キーボード入力
(0 55 6765 832040 102334155)    # 表示結果
```

#特定のSchemeファイルを読み込んで評価した後，Scheme記述を文字列として渡した評価結果を文字列として受け取る例

```C:gauche-initfile.c
#include <stdio.h>
#include <gauche.h>

#define APPINITSCM "~/.appinit.scm"
#define S_NAME     "(cadr (assq 'name    *my-profile*))"
#define S_TWITTER  "(cadr (assq 'twitter *my-profile*))"

const char* getScmString(ScmObj *obj)
{
  ScmObj sStr, oPort = Scm_MakeOutputStringPort(TRUE);
  Scm_Printf(SCM_PORT(oPort), "%S", obj);
  sStr = Scm_GetOutputString(SCM_PORT(oPort), 0);
  return Scm_GetStringConst(SCM_STRING(sStr));
}

int main(int argc, char **argv)
{
  GC_INIT(); 
  Scm_Init(GAUCHE_SIGNATURE);

  ScmLoadPacket lRet;
  if(Scm_Load(APPINITSCM, 0, &lRet) < 0) {
    printf("There is no %s.\n", APPINITSCM);
    return (1);
  }

  ScmEvalPacket eRet;

  if (Scm_EvalCString(S_NAME, SCM_NIL, &eRet))
    printf("Name: %s\n", getScmString((ScmObj *)eRet.results[0]));
  if (Scm_EvalCString(S_TWITTER, SCM_NIL, &eRet))
    printf("Twitter: %s\n", getScmString((ScmObj *)eRet.results[0]));

  Scm_Exit(0); 

  return(0);
}
```

特定のSchemeファイルの例は次の通り．いわゆる設定ファイルを模したもの．

```
$ cat ~/.appinit.scm 
(define *my-profile*
  '((name "TAKIZAWA Yozo") (twitter "@ytaki0801")))
```

コンパイルおよび実行例は次の通り．

```bash
$ cc -Wall `gauche-config -I` -o gauche-initfile gauche-initfile.c `gauche-config -l`
$ ./gauche-initfile 
Name: "TAKIZAWA Yozo"
Twitter: "@ytaki0801"
```

#特定のSchemeファイルを読み込んで評価した後，C言語内でリスト構造を作成して引数とし，手続きを`apply`で実行した結果を整数値として受け取る例

```C:gauche-call.c
#include <stdio.h>
#include <gauche.h>

#define APPINITSCM "~/.appinit.scm"

int main(int argc, char **argv)
{
  GC_INIT(); 
  Scm_Init(GAUCHE_SIGNATURE);

  ScmLoadPacket lRet;
  if (Scm_Load(APPINITSCM, 0, &lRet) < 0) {
    printf("There is no %s.\n", APPINITSCM);
    return (1);
  }

  int x = 40;
  if (argc == 2) x = atoi(argv[1]);

  ScmEvalPacket eRet;
  static ScmObj proc = SCM_UNDEFINED;
  SCM_BIND_PROC(proc, "fib", Scm_UserModule());
  ScmObj args = SCM_LIST3(SCM_MAKE_INT(x),SCM_MAKE_INT(0),SCM_MAKE_INT(1));
  if (Scm_Apply(proc, args, &eRet) < 0) {    // (apply fib '(40 0 1))
    printf("Error in S expression\n");
    return (1);
  }
  printf("fib(%d) = %ld\n", x, SCM_INT_VALUE(eRet.results[0]));

  Scm_Exit(0); 

  return(0);
}
```

特定のSchemeファイルの例は次の通り．新規に関数`fib`を定義している．

```
$ cat ~/.appinit.scm 
(define fib (lambda (n f1 f2) (if (= n 0) f1 (fib (- n 1) f2 (+ f1 f2)))))
```

コンパイルおよび実行例は次の通り．

```bash
$ cc -Wall `gauche-config -I` -o gauche-call gauche-call.c `gauche-config --static-libs`
$ ./gauche-call
fib(40) = 102334155
$ ./gauche-call 0
fib(0) = 0
$ ./gauche-call 5
fib(5) = 5
$ ./gauche-call 10
fib(10) = 55
```

#（以下，必要に応じて追加）

#備考

##記事に関する補足
* 本当は[GNU Guile](https://www.gnu.org/software/guile/)の方をまとめたかったんですが（だってそのために作られてるんだし），公式サイトの[割と本格的な組み込み例](https://www.gnu.org/software/guile/docs/guile-tut/tutorial.html)か[初期化サンプル](https://www.gnu.org/software/guile/manual/html_node/A-Sample-Guile-Main-Program.html#A-Sample-Guile-Main-Program)しか見つからなかったので，相応の例がさっくり用意できた[Gauche](https://practical-scheme.net/gauche/index-j.html)の方からまとめて，その例に対応するGuile記述例を整理していきたいかなとか．言い訳終わり．【2020-08-18追記】あ，[GNU Guileの場合の手頃な解説ページ](https://www.ibm.com/developerworks/library/l-guile/index.html)見つけた．古いけど．そのうちGNU Guile版も追記してタイトル変えるかも．→[新規記事](https://qiita.com/ytaki0801/items/83f8bc510342b7945519)にしました．
* あくまでサンプル記述なので，今後もごく簡単な例しか載せない予定です．でも，これらだけでも，結構実用的なんじゃないかなあ．二番目の設定ファイル用途とか，自作アプリに静的ライブラリ＋Schemeライブラリ数MB程度追加で組み込めますよ，どうですか（謎のセールス）．
* もともとこの記事を書こうと思ったきっかけは，Lisp-1（Scheme） vs Lisp-2（Common Lisp）骨肉の争いをネットで見たので．個人的にはシンプルなLisp-1派だけど，それはあくまで好みの問題で，実用面ではいろんな言語やライブラリを分野向き不向きで使い分けていけばいいんじゃないかなあと．そういう意味では，Common Lispで書かれたSchemeインタプリタを開発ソフトウェアのフロントエンドにして…いや，Julia処理系みたいにバックエンドは効率と実績を重視してC/C++中心に…あれ，そういえばGNU GuileやGaucheがもともとそうじゃん．…という壮大な寄り道連鎖から本記事に至りました．寄り道過ぎる．でも，C言語ソースコードにScheme記述が混ざってるのってなんかいい感じ．正規表現ライブラリみたいで（錯乱中）．
* ああ，Pythonも[インタプリタ埋め込み](https://docs.python.org/3/extending/embedding.html)を想定している…けど，やはりというか，インタプリタ拡張に比べてマニュアル記述が圧倒的に少ないか．APIは拡張方法を見ればわかるということかな．いやでも，Pythonはフロントエンドというよりはバックエンドかなあ（偏見）．ユーザがPythonプログラミングで凝った設定や機能拡張を行うことを想定するアプリケーション…Schemeでも同じか．あ，PythonプログラムでScheme記述できるようにするってのはどうかな．ほら，lambda式や高階関数使いたかったらSchemeコード書いてくれみたいな．ダメか．

##参考文献
* [Gauche - A Scheme Implementation](https://practical-scheme.net/gauche/index-j.html)
* [GNU Guile](https://www.gnu.org/software/guile/)
* [Scripting with Guile](https://www.ibm.com/developerworks/library/l-guile/index.html)

##更新履歴
* 2020-08-19：[Python3版の記事](https://qiita.com/ytaki0801/items/9d30c65e5edba7dcb0fe)リンク追記
* 2020-08-18：例その３を追加，記事に関する補足追記，[GNU Guile版記事](https://qiita.com/ytaki0801/items/83f8bc510342b7945519)リンク追記
* 2020-08-17：初版公開（例その１，例その２，記事に関する補足，他）