今年読んで良かった書籍は、「Go言語でつくるインタプリタ」でした。
2020.12.31
この記事は最終更新日から1年以上が経過しています。
どもです。
いやぁ。。
2020年も今日で終わりと言うことで、今年も本当にあっと言う間だったなぁ。といった感想。
今年は何より、コロナの話題で持ちきりの一年でしたね。
これまでにない状況の中、色々と変化を求められた年になりましたね。
今年は今までに無いくらいに家にいる時間が多かった年でした。
そのお陰か、積み本となっていた本を消化していくことも出来たし、更なる積み本も増えて行きましたw
更に、積みゲームも増えたりと。。と、いくら時間があっても足りないくらいになってしまいました。
引き篭もる時間が増えた年だったが、まだまだどんだけでも引き篭もるのが分かった年でしたw
そんな中、積みに積まれた技術書の中から、今年良かったなぁと思った技術書籍を1冊ご紹介させていただきます。
と、すでにタイトルに書かれていますが、オライリーから出版されている「Go言語でつくるインタプリタ」でした。
販売されたのは、2018年6月で、評価も高く色々と話題になっていたのですが、今年ようやく読めました。
何をする本なのかと言われますと、タイトルそのまんまなのですが、go言語で独自のMonkeyと言われるインタプリタ言語を作成して行きます。
今まで、プログラム言語作成も行った事がなかった私としては、非常に分かりやすく為になる書籍でした。
同じように、プログラム言語やコンパイラーなど作成の経験のない方などには、取り掛かりとしては入りやすい内容でお勧め出来る書籍です。
Go言語でつくるインタプリタ
内容は以下の通り、
- C言語の構文
- 変数束縛
- 変数と真偽値
- 算術式
- 組み込み関数
- 第一級高階関数
- クロージャー
- 文字列データ型
- 配列データ型
- ハッシュデータ型
- 字句解析器
- 構文解析器
- 抽象構文木(AST)
- 内部オブジェクトシステム
- 評価器
と言った機能を持ったインタプリタ言語「monkey」を作成して行きます。
1章 字句解析
まず、字句解析ですが、
ソースコード → トークン列 → 抽象構文木
と言った流れで解析できるように作成して行きます。
トークン列は、以下の様に、
const ( ILLEGAL = "ILLEGAL" EOF = "EOF"</code> // 識別子 + リテラル IDENT = "IDENT" // add, foobar, x, y,... INT = "INT" // 123456 // 演算子 ASSIGN = "=" .... )
と言った具合にトークン列を作成し、字句解析器を用いて解析。
字句解析器(レキサー)
定義したトークン列を解析するための字句解析器を作成して行きます。
func (l *Lexer) NextToken() token.Token { var tok token.Token</code> switch l.ch { case '+': tok = newToken(token.PLUS, l.ch) case '-': tok = newToken(token.MINUS, l.ch) ....
2章 構文解析
2章は構文解析に取り掛かります。
ソースコードの内部
- 構文木 Syntax Tree
- 抽象構文木 Abstract syntax tree AST
パーサージェレレータに関する事、
CFGの記法
- バッカスナウア記法 Backus-Naur Form: BNF
- 拡張バッカスナウア記法 Extended Backus-Naur Form: EBNF
構文解析2つの戦略
- トップダウン構文解析
- ボトムアップ構文解析
トップダウン構文解析
- 再帰下降構文解析 recursive descent parsing
- アーリー法 Early parsing
- 予測的構文解析 predictive parsing
などと言った解説なども含まれており、非常に解析器に関して為になる事がいっぱい記されていました。
今回の書籍では、再帰下降構文解析→トップダウン演算子優先順位の方式となり、「Pratt構文解析」をベースにASTノードを構築できる仕組みを作って行きます。
3章 評価
3章では、REPL の E(eval) として Monkey のソースコードを評価する評価器の作成となります。
関数、関数呼び出しでMonkeyの基本を作成し、オブジェクトシステムを作成して行きます。
この章を終えた頃には、第一級関数やクロージャーのような機能まで実装出来ています。
4章 インタプリタの拡張
この章では、データ型と関数、文字列、配列を字句解析器から振り返り、配列やハッシュなどに対応していく流れになります。
と、ざっくり説明してきましたが、もっと詳細書いてしまうとすごく長くなりそうなので(途中で気が付いた)、改めていつか纏めたいなと思う次第です。
良かった点
基本的には、写経するだけで最後には、インタプリタが実装出来ると言う流れになるのですが、良かったのが、テスト駆動開発で進めていく点です。
このような感じで、
lexer/lexer_test.go
func TestNextToken(t *testing.T) { input := `=+(){},;` tests := []struct { expectedType token.TokenType expectedLiteral string }{ {token.ASSIGN, "="}, {token.PLUS, "+"}, {token.LPAREN, "("}, ....
失敗(red)のテストを書き、成功(green)のソースコードを書くと言う流れなので、「なんでか動かない」と言った状況があまり発生せずに、どんどん進められていけるでしょう。
あと、何より要所要所の「エンカレッジ」が凄いw
- 素晴らしい!
- 簡単だ!
- 完璧だ!
- 難しくない。
すぐ言ってくれますw
- 素晴らしいニュース
すぐ、良いニュースが入ります。w
こう言った感じで背中を押してもらえる事で、最後までモチベーションを下げずに取り掛かれます。
あと、Go言語と言うのが良かった。Go言語のシンプルさを十分に生かし、再びGo熱が出たのもこの本きっかけでした。
益々、Go言語が好きになりました。
また、最後まで完成させる時間が丁度いいところですかね。
自分の場合は10日間くらいでした。(git commit開始から)
と言うのも、この時期めちゃくちゃスプラトゥーン2にハマっていまして(再び)、毎日2時間以上はプレイしてしまい、その結果、総合プレイ時間が400時間以上となって(もう500時間)しまったのですが、
そんな日々の中進めていても10日間くらいだったので、集中すれば1週間もあれば終わるのでは無いでしょうか?
また、そんな中やっていたので、覚えも中途半端なところがあるので、来年は復習も兼ねて集中して再びやりたい。
と言う事で、駆け足で「Go言語でつくるインタプリタ」を紹介させて頂いたのですが、来年、再度作成しインタプリタの仕組みを纏めた記事をアップできればと思っております。
また、来年こそは、本来作成したかったOSやエミュレーター作成に入れればなぁと思う大晦日でした。
では、来年もよろしくお願いしますー!!