Open5

Ribbon: Cでプロトタイプする

okuokuokuoku

いきなり C++ で作り出しても C++ のオーバーヘッドなのか自分の書いたコードが悪いのか区別が付かなそうなので、C++でやろうとしていることをCによる手動で実装しておくことにした。

アルゴリズムはC++でやろうとしているのと同じ、 リファレンスカウント + 循環参照検出 とする。ある程度できてきたら、Espuinoと同じ固定長オブジェクト方式の実装も用意したい。

http://www.espruino.com/Internals

okuokuokuoku

ヒープの設計

数値型は32/64bitsの固定巾としてタグの埋め込みは行わず、型を表現するビット群は別の配列に確保することにする。これにより、浮動小数点数をヒープオブジェクトにする必要がなくなるが、型を他所のメモリに置くことでメモリ負荷(ポインタの演算負荷)が増えるというデメリットがある。

ヒープに置かれるオブジェクトは参照カウントのみのオブジェクト(文字列等)と、循環参照検出の対象になるオブジェクト(Vector等)に分かれることになる。

C言語にはスタック上の参照をスキャンするためのポータブルな方法は存在しないので、専用の 参照スタック を用意し、明示的に確保させる。Scheme側のヒープを参照するCコードは、

  1. スタックフレームの作成(enter)
  2. スタックフレームの破棄(leave)

の操作を関数の先頭および return 直前にやる必要がある。enterとleaveの数は1対1対応しなくても良い。例えば、 longjmp で本来行われるleave処理を飛ばしてしまっても、自分が実施したenterまでのスタックは解放しても良いことが自明となる。 (C++では、longjmp のかわりに trycatch を使えばデストラクタの呼出しで代替できる。)

okuokuokuoku

型のunwrap

ribに格納しなくても良い型は格納しないことにした。Scheme上なら良いけど、Cでいちいちribから実データを取り出すコードを書くのが面倒なため。

https://github.com/okuoku/yuniribbit-proto/commit/1ce8883a6d440ce2abe2fe0e6a70a232a5923e85

https://github.com/okuoku/yuniribbit-proto/commit/c327f6191788c917880e61603a41eeaddf797812

ただし、 vector? はunwrapできない 。これはR7RS Schemeのtype disjointルールに因る。ある1つのオブジェクトについて、例えば同時に procedure?vector? が真になってはいけない。

3.2. Disjointness of types
No object satisfies more than one of the following predicates:

boolean? bytevector?
char? eof-object?
null? number?
pair? port?
procedure? string?
symbol? vector?

Scheme上のVM実装では rib の表現にvectorを使用しているため、vectorをunwrapしてしまうと、全ての rib に格納されるタイプのオブジェクトについて vector? が真になってしまう。

... いや vector だけは特別扱いしても良いかな。。

okuokuokuoku

とりあえずGC以外のオブジェクトシステムを実装

シリアライズしたSchemeオブジェクトをヒープ上に展開するところまで実装しておいた。これにVMとプリミティブを付ければインタプリタになるはず。

https://github.com/okuoku/ribbon/commit/59dccca9d22d3e2df1ad3a6e5ba7fa2239fa219b

すでに20KiB超えてんだけど。。しかも、ビルドは通るけど結構バグっている。

https://github.com/okuoku/ribbon/commit/2fc32581671021994c3590f56d13bb5f7a63fd6b

https://github.com/okuoku/ribbon/commit/4817fe96d7937bbe5b32fc9bebea3d9c7e8b2105

こんな激烈に間違っててエラーなく動いちゃうのか。。(実際のプログラムを流しているわけではないのでまだ間違った事が起きてそう)