Go 言語でインタプリタを作成する。スクラッチでプログラムを作ることで、 プログラミング言語とインタプリタのしくみを実践的に学ぶことができる。
本書で作って見せるインタプリタは Monkey というなまえの言語である。 この Monkey を作るための言語として、本書ではプログラミング言語 Go を用いている。では、Monkey を作るには、ほかの言語でできるのだろうか? 「はじめに」の xii ページで次のように著者は言っている。
それにも増して重要だと思うのは、本書で提示する Go のコードは、 他の言語でもほとんど同じように書けるということだ。 おそらく C や C++、Rust のように、 より低レベルな言語でも大丈夫だ。
私はこのことばを信じて、本書を理解したら C で Monkey を作ろうと思ってしまった。 ところが、「3.11 ゴミを片づけているのは誰か」の節を読み、がっかりしてしてしまった。 というのも、Monkey のガベージコレクタ(GC)は Go 言語の GC を使っている、 ということが書かれていたからだ。 そして、これを受けて p.173 では次のように注意している。
もし C のような言語でインタプリタを書こうとしていたら、 C には GC がないので、自分自身でそれを実装して、 インタプリタのユーザのためにメモリを管理する必要がある。
ということは、事実上 C や C++ で Monkey を実装するのは不可能ではないか、と思った次第だ。 もちろん、Boehm GC ライブラリを使うという方法はあるだろうが、 意欲が一度に下がってしまったことも事実である。
なお、この Monkey を Rust で実装した方々がいる。Rust の資源管理は GC とは別の手法によるようだが、実装できているようなので、うまくやっているのだろう。
p.92 でテストを行ったところ、本とは異なるエラーが出てきた。これは、 テストの失敗以前に、コンパイルエラーとなっている個所である。
$ go test ./parser
(中略)
parser/parser_test.go:216:42: tt.integerValue undefined
(type struct { input string; operator string; value interface {}}
has no field or method integerValue)
parser/parser_test.go:288:41: cannot use tt.leftValue (type interface {})
as type int64 in argument to testIntegerLiteral: need type assertion
parser/parser_test.go:297:42: cannot use tt.rightValue (type interface {})
as type int64 in argument to testIntegerLiteral: need type assertion
(後略)
結局、これらのエラーの出現個所での関数呼び出しをコメントアウトするしかなかった。 本書に記載されている、アーカイブからダウンロードされたソースコードをみると、 該当部分の記載がない。テストに応じて、記載をしたり省いたりということなのだろう。
私も、ほかの方々と同様、本にあるコードを手で打ちながら、 動作を確認した。私の動作環境は、Ubuntu (正確には Windows Subsystem for Linux の Ubuntu 20.04) の、go1.13.8 linux/amd64 である。 本書の通りに入力したはずなのに、どうしても同じように動作しないという個所がいくつかあったが、 よく見ると私のタイプミスだった。よくあったのが () と {} の打ち間違いである。 私は Go 言語の文法を全く知らないので、この打ち間違いはよくあった。
一つ恥ずかしかったのは、4.2 文字列の 4.2.3 文字列の評価のところだ。 本書の通り入力したのだが、結果が異なる。まず、正しく失敗するはずの個所だ。pp.181 では次の通り。
$ go test ./evaluator --- FAIL: TestStringLiteral (0.00s) evaluator_test.go:317: object is not String. got<nil> (<nil>) FAIL (後略)
一方、私の結果は次の通り。
--- FAIL: TestStringLiteral (0.00s) evaluator_test.go:330: object is not String. got=*object.Error (&{Message:identifier not found: Hello}) FAIL (後略)
まあ、同じ失敗だし、いいか、と思っていたら、全くよくないのだった。その後、 evaluator.go を更新して完成させても、私の環境ではエラーのままである。 一晩明けて、本書を見直してようやく私の失敗がわかった。本書では、テストコードにこう書かれている。
input := `"Hello World"`
一方、私の入力は次の通りだった。
input := "Hello World"
つまり、バッククォーテーションを抜かしていた。これでは誤ったエラーになるのも無理はない。
もうひとつ、こんなエラーもあった。 p.199 の最後のテストでエラーが出る。
$ go test ./parser --- FAIL: TestOperatorPrecedenceParsing (0.00s) parser_test.go:440: expected="((a * ([1, 2, 3, 4][(b * c)])) * d)", got="((a * ([1, 2, 3, 4][(b * c)]) * d)" parser_test.go:440: expected="add((a * (b[2])), (b[1]), (2 * ([1, 2][1])))", got="add((a * (b[2]), (b[1], (2 * ([1,2][1]))" FAIL
p.198 でテストが通らないとされた本書のエラーは、私が得たエラーと同じである。
parser_test.go:393: expected="((a * ([1, 2, 3, 4][(b * c)])) * d)", got="(a * [1, 2, 3, 4])([(b * c)] * d)"
何がいけないのだろう。しばらくして、ast.go で次のように書くべきところ
func (ie *Index Expression) String() string { var out bytes.Buffer out.WriteString("(") out.WriteString(ie.Left.String()) out.WriteString("[") out.WriteString(ie.Index.String()) out.WriteString("])") }
誤って次のように書いてしまったのだ。
func (ie *Index Expression) String() string { var out bytes.Buffer out.WriteString("(") out.WriteString(ie.Left.String()) out.WriteString("[") out.WriteString(ie.Index.String()) out.WriteString("]") }
付録はマクロシステムである。どうにかこうにか入れ終えて、最後に到達した:
>> unless(10 > 5, puts("not greater"), puts("greater"));
本書にあるとおり、1行だけ greater と表示されることを予期していた私は、 少しずっこけた。表示は2行にわたっていた。
greater
null
でも、この null は出て当然のはずだ。というのは、
p.230 で、REPL では puts から期待される出力に加えて null も表示される。
と書かれているからだ。
私は毎日8時間ほどを充てて本書の写経に励んでいたが、写経速度が遅いことに加え、 打ち間違いによるエラーが続出し、結局すべてを稼働させるのに付録を含めて1週間を要した。 年寄りである。
なお、これだけ写経したにもかかわらず、Go 言語についてはほとんど理解できていない。
p.88 上から5行め、「真偽値リテラルを実装する方法ついて」とあるが、 「真偽値リテラルを実装する方法について」が正しいだろう。
p.242, evaluator/quote_unquote.go のソースコードでは、 冒頭にパッケージ宣言 package evaluator が抜けている。 同様に、p.247 , ast/modify.go のソースコードでは、 冒頭にパッケージ宣言 package ast が抜けている。
書名 | Go 言語でつくるインタプリタ |
著者 | Thorsten Ball |
発行日 | |
発行 | オライリー・ジャパン |
発売 | オーム社 |
定価 | 3400 円(本体) |
サイズ | 判 ページ |
ISBN | 978-4-87311-822-2 |
その他 | 越谷市立図書館で借りて読む |
まりんきょ学問所 > コンピュータの部屋 > コンピュータの本 > コンパイラ・インタープリター > Thorsten Ball : Go 言語でつくるインタプリタ