ヒープはとりあえず適当にfakeして(= Scheme上のオブジェクトで同等機能を実装することにして)ランタイムライブラリの実装に進む。ヒープ実装はバグりやすいので、一旦他の部分の品質が担保できる状態になってからネットリと進めるのが良い気がしている。

ランタイムライブラリは基本的に退屈なパートで、かつ、品質に対する要求も高いので可能な限り再利用性とテスト可能性に配慮して作る。



ランタイムライブラリの移植層、つまり、図中のHeap + Coreがyuniが想定する必要最低限のScheme手続きということになる。ヒープを実装する場合は、Heap + Coreに含まれる手続きを全て提供しなければならない。

... 一般的なScheme処理系と比べてもちょっと多い気がするが、Scheme標準の構文をほぼ全て実装できる内容とするためにかなりリッチになってしまった。例えば、R7RSに含まれるcase-lambdaを実装するためには、lengthとか比較演算が必要( https://github.com/okuoku/yuni/blob/cce868a69f3e265134eac73a90b977c3197ac05f/lib-r7c/r7c-report/misc/case-lambda.sls#L23 )になる。

(ほぼと付いているのは、parametizeを含めていないため。R7RSに含まれる参照実装ではdynamic-windを使って実装しているが、処理系のプリミティブとして実装する手もあるし、そもそもyuniでは必要としていないので省略した。あとdefine-record-typeも含めていない。)

今回、lengthや比較演算はfixnum専用のものを別途定義してそちらを要求することにした。長さがbignum表現されるようなリストを少なくとも構文では使わないし、Scheme側でbignumを実装しようとすると不都合なため。この手のScheme規格から見たサブセットは先頭に $ を付けて区別している。(simple-structのようなyuni独自仕様は特にそのような変更をしていない)

$boolean=?のような比較は2要素の比較だけをサポートする(3要素以上はScheme側で実装すれば十分なため)。$make-vector等のmake系は、初期値の設定機能を省略している。つまり、基本的にHeap + Coreには可変長引数の手続きが含まれないように配慮している。例外はapplyやerror等で、これらはあんまり真面目に考察していない。特にerrorやraise系は分離した方が良いような気もしているが。。

Heap + Coreが必ずしも構文の実装に必要なものだけというわけではなく、前回( http://d.hatena.ne.jp/mjt/20170530/p1 )書いたようなScheme型を実現するために必要最低限のものを含んでいる。例えばbytevectorは構文には不要だがヒープがbytevectorを提供しないわけには行かないので含めている。

意外と芋蔓式に入るものが多い。caseの実装にはmemvが必要になり、memvの実装にはeqv?が必要となり、eqv?の実装のためにはboolean=?のような手続きも必要となる。逆に、R7RSでは文字列に対するeqv?を規定していない(!?)ので、ここにはstring=?を含めていない。