Ruby 0.49 をビルドする

再度、ちょっとした考古学的興味により初期のRubyを調べてみたくなったので、Ruby 0.49 をビルドして動かしてみた。

# プログラミング考古学シリーズ

* 第1回: [Python 1.0 をビルドする](https://qiita.com/hakatashi/items/e0dab15d96334cadc055)
* **第2回: Ruby 0.49 をビルドする**
* 第3回: [Perl 1.0 をビルドする](https://qiita.com/hakatashi/items/5c41b3835814e13a7b6a)

# ソースコード入手

Rubyが初めてインターネットに公表されたのは1995年12月21日、バージョンは**0.95**でのことである [(fj.comp.oops)](https://groups.google.com/d/msg/fj.comp.oops/D6cdtizkucU/b45aWRVTU6sJ)。またfjへ投稿される前にもいくつかのクローズドアルファが公開されており、遡って **Ruby 0.65** などのバージョンが公開されていた [(Rubyist Magazine)](http://magazine.rubyist.net/?0021-Hotlinks)。

それ以前のソースコードは長らくmatzが個人的に保管していたが、2006年にmatzがバージョン**0.49**以降のソースコードを公開して以来 [(rubi-talk)](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/206778)、0.49が現存する最も古いRubyのソースコードとして知られている。[私家版Ruby史](http://metanest.jp/ruby-legends/)では**1994年6〜7月頃**のコードと推定されており、Rubyが命名されてから1年と少しのころのコードの模様である。

公開されたバージョン0.49のソースコード (ruby-0.49.tar.gz) は以下から入手できる。

ftp://ftp.ruby-lang.org/pub/ruby/1.0/

しかしこのコード、現在の環境ではエラーが多発してまるでコンパイルできない。時期的には前回試した Python 1.0.1 とほぼ同時期のはずだが、依存するライブラリが入手困難なものが多く、簡単にはビルドまで辿り着けない。

が、世の中には~~酔狂な人~~頼もしい同士がいるもので、現代の環境でRubyを動かすための**historical-rubies**というパッチが、Charlie Somerville さんによって公開されている。

https://github.com/charliesome/historical-rubies

というわけで、さっそくビルドしてみる。

# ビルド

Ubuntu 64bit 環境を想定。

historical-rubiesのパッチでは32bitバイナリをターゲットにしているので、クロスコンパイル用の環境をセットアップする。

```sh
sudo dpkg --add-architecture i386
sudo apt update
sudo apt install gcc-multilib
```

依存ライブラリをいくつかインストールする。

```sh
sudo apt install libgdbm-dev:i386 libssl-dev:i386 bison
```

続いてソースコードを入手し、パッチを当てる。

```sh
curl http://ftp.ruby-lang.org/pub/ruby/1.0/ruby-0.49.tar.gz -LO
tar xzf ruby-0.49.tar.gz
cd ruby
curl https://raw.githubusercontent.com/hakatashi/historical-rubies/master/ruby-0.49.patch -LO
patch -p1 < ruby-0.49.patch
```

で、あとは頑張ってビルドするだけ。

```sh
./configure
sed -i 's/@//' Makefile
make LIBS="-L/usr/lib32 -lm -lgdbm -lgdbm_compat -lcrypt"
```

なお、[テスト用に作成したDockerfile](https://github.com/hakatashi/esolang-box/blob/e923e2c/boxes/ruby0.49/Dockerfile)があるので参考にしてもらいたい。

# 動かしてみる

出てきたバイナリファイルのrubyを使って、少し遊んでみる。

ではさっそくおなじみの hello world から⋯⋯。

```
$ ./ruby <<<"puts 'Hello, World'"
-:1: syntax error
```

文法エラーとなる。どうやらカッコを省略できないようだ。

そこでカッコつけて色々と試してみるが⋯⋯。

```
$ ./ruby <<<"puts('Hello, World')"
Segmentation fault
$ ./ruby <<<"p('Hello, World')"
Segmentation fault
$ ./ruby <<<"STDOUT.write('Hello, World')"
Segmentation fault
```

うまくいかない。これは本当にRubyなのか?

正しい文字出力関数は**print**である。

```
$ ./ruby <<<"print('Hello, World')"
Hello, World
```

現代では「多様性は善」などと言われるRubyも、さすがにこの頃はまだやり方が一つしかなかったようだ。

# sampleを眺めてみる

tarballに含まれているsamplesディレクトリの中には、この頃動いていたサンプルコードが多数含まれている。

いくつか興味深いものを拾ってみよう。

```fib.rb
def fib (n)
  if n<2
    n
  else
    fib(n-2)+fib(n-1)
  end
end
print(fib(20), "\n");
```

暗黙の返り値やif文など、「すべてがオブジェクト」というRubyの思想がこの頃からしっかり設計されていたことがわかる秀逸なコード。

また、インデントはこの頃からスペース2つだったようだ。

```math.rb
#load("lib/math.o")
include Math
sqrt(4)
print(Math.sqrt(257), "\n")
```

トップレベルにMathモジュールをincludeして実行している。現代でも動くコードだが、Ruby 0.49 で動かすとなぜかsqrtではなくlog10した結果が帰ってくる。それもそのはずで、該当部分のコードには、

```math.c
static VALUE
Fmath_sqrt(obj, x)
    VALUE obj;
    struct RFloat *x;
{
    Need_Float(x);
    return float_new(log10(x->value));

    if (x->value < 0.0) Fail("square root for negative number");
    return float_new(sqrt(x->value));
}
```

と書かれている。matzがデバッグを行っていた痕跡だろうか。

```attr.rb
class Foo
  attr("test", %TRUE)
end

foo = Foo.new
foo.test = 10
print(foo.test, "\n")
foo._inspect.print
print("\n")
```

`%TRUE`という謎の識別子が登場する。ソースコードを読む限りTRUEを表す定数だった模様。

```tt.rb
module Print
  print("in Print\n")
  def println(*args)
    for a in args
      print(a)
    end
    print("\n")
  end def

  def println2(*args)
    print(*args)
    print("\n")
  end def
end module
```

`end def` や `end module` など、今からだと考えられない文法が用いられており震える。matzはRubyの`end`をいたく気に入っているようだが、先発のVerilog (1984年) などの影響を受けてのものだったのだろうか。(なお、[Rubyに影響を与えた言語](http://gihyo.jp/news/report/01/rubykaigi2013/0001)の中で、構文にendを用いるのはPASCALのみである)

# おまけ: Ruby 1.0 の場合

```
wget http://ftp.ruby-lang.org/pub/ruby/1.0/ruby-1.0-971225.tar.gz
tar xzf ruby-1.0-971225.tar.gz
wget https://github.com/charliesome/historical-rubies/raw/master/ruby-1.0-971225.patch
cd ruby-1.0-971225
patch -p0 < ../ruby-1.0-971225.patch
./configure
make DLDFLAGS="-shared -melf_i386"
```

# 参考資料

* [326 種類の Ruby をビルドする方法 ~ 0.49 から 2.6.0-preview1 まで ~](http://www.a-k-r.org/pub/2018-06-02-all-ruby-ruby-kaigi-after-party.pdf)

# 次回

[Perl 1.0 をビルドする](https://qiita.com/hakatashi/items/5c41b3835814e13a7b6a)