さっきの続き。


eval時に行うmalloc(的な何か)は、eager型とlazy型を選べるようにする。

  • eager型evalがさっき書いた、eval時に一括で申請して、領域が無くなったらエラー例外を投げるもの。
  • lazy型evalは普通に、eval時には何もせずに、cons等の度にmalloc(的な何か)を行い、malloc(的な何か)が失敗した段階でエラー例外を投げるもの。


それぞれのevalが入れ子になった場合の挙動は、基本的に、外側のevalに準じるようにする。具体的には以下のようになる(文章にすると分かりにくいが、多分直感的だとは思う)。

  • eager型evalの中でeager型evalが実行された場合、内側evalの最初に、外側evalが確保した領域の中から一括で申請し、そこから使っていく。
  • lazy型evalの中でeager型evalが実行された場合、内側evalの最初に、外側evalのGC_MALLOCを通して一括でメモリを申請し、その中から使っていく。
  • eager型evalの中でlazy型evalが実行された場合、内側のeval内でメモリが必要になった個々の段階で、外側のevalが確保したメモリ内から使っていく。
  • lazy型evalの中でlazy型evalが実行された場合、内側のevalは、外側evalのGC_MALLOCを通して、個々にメモリを申請していく。


追加で、以下の特性を持たせる。

  • どのevalでも、メモリがなくなった時はevalを終了し、「外側」にエラー例外を渡す。
    • これはevalが入れ子になっている場合、gcによって(外側のevalの)メモリが回復する事が期待できるので、外側のevalは普通に生き残れる。
  • デフォルトでは、一番外側のevalはlazy型で動くものとする。


……という仕様が良さそうだ。


問題は、Gaucheでは、

/* Fundamental allocators */
#define SCM_MALLOC(size)          GC_MALLOC(size)
#define SCM_MALLOC_ATOMIC(size)   GC_MALLOC_ATOMIC(size)

#define SCM_NEW(type)         ((type*)(SCM_MALLOC(sizeof(type))))
#define SCM_NEW_ARRAY(type, nelts) ((type*)(SCM_MALLOC(sizeof(type)*(nelts))))
#define SCM_NEW2(type, size)  ((type)(SCM_MALLOC(size)))
#define SCM_NEW_ATOMIC(type)  ((type*)(SCM_MALLOC_ATOMIC(sizeof(type))))
#define SCM_NEW_ATOMIC_ARRAY(type, nelts)  ((type*)(SCM_MALLOC_ATOMIC(sizeof(type)*(nelts))))
#define SCM_NEW_ATOMIC2(type, size) ((type)(SCM_MALLOC_ATOMIC(size)))

のように、直にBoehm GCGC_MALLOCが設定されているので、
ここを直さない限りは、eager型の挙動にする事は出来ない。
しかし逆に見れば、ここで一旦マクロラッピングされているので、ここを直すだけで動く可能性はある、という事でもある。
とは言え、このメモリ領域の情報をどこに持たせるかと考えると、

  • eval時に専用領域を用意する
  • R5RS等でいう「環境」に持たせる

のどっちかだろうが、どちらにしても、単純にここだけいじって動くという事にはまずならないだろう……。


これを真面目に開発すべきかは、ちょっと悩む。
これを実装すれば、eval/svと一緒に使って、とりあえず「安全なeval」をGauche上で機能させる事が可能になる。
が……これ難易度ちょっと高くないか?