コンパイラ作成(1) 字句解析
暇なんで何かプログラムでも作ってみるよ。さて何を作るかってしばらく悩んでコンパイラを作ることにしたよ。どんな言語にするかは今のところ何も決めてないんだけど、C言語っぽいやつにしてみた。言語名はmyc。
開発言語はruby。別になんだって良かったんだけどね。rubyはちょこちょこっとしたスクリプトを組むときに使ってるぐらいであんまり詳しくはないんだけど、ruby自体の勉強も兼ねて頑張ってみるよ。それとインタープリタ言語の方が試しながらちょっとづつ作ってくのに適してるからね。それでコンパイラを作るってのは変な話だけどさ。
で、まずは字句解析から。
最初は単純なソースから始めるよ。
foo = 10 + 20 - 15; bar = "hello, world!";
ではプログラム開始。rubyでclassを使ったことなかったんでその辺の勉強からやんないと駄目だったけど、なんとかできたよ。
fname = ARGV[0] lex = Lexer.new(fname) i = 0 loop do i += 1 kind, str = lex.gettoken if kind == TK::EOF then break elsif kind == TK::ID then print i," TK::ID ",str,"\n" elsif kind == TK::NUMBER then print i," TK::NUMBER ",str,"\n" elsif kind == TK::STRING then print i," TK::STRING ",str,"\n" elsif kind == TK::SYMBOL then print i," TK::SYMBOL ",str,"\n" elsif kind == TK::UNKNOWN then print i," TK::UNKNOWN ",str,"\n" break end end lex.close
lex = Lexer.new(fname)で字句解析クラスのインスタンス生成。ここでファイルのオープンと最初の行の先読みをしてる。
lex.closeは単純にファイルをクローズしてるだけ。
kind, str = lex.gettokenでトークンを一個取り出してる。
gettokenの中身は素朴に文字を順番に見てってるだけ。空白や空行の処理がちょっと面倒なだけで100行ちょっとでできたよ。
~/myc$ ./myc.rb a.myc 1 TK::ID foo 2 TK::SYMBOL = 3 TK::NUMBER 10 4 TK::SYMBOL + 5 TK::NUMBER 20 6 TK::SYMBOL - 7 TK::NUMBER 15 8 TK::SYMBOL ; 9 TK::ID bar 10 TK::SYMBOL = 11 TK::STRING hello, world! 12 TK::SYMBOL ;
動かしてみたけどちゃんと動いてるみたい。今回作った分だとサポートしてる演算子は+、-、=だけ。文字列リテラル中のエスケープシーケンスの処理も入ってないなあ。コメントのスキップ機能とかちゃんとしたコンパイラに仕上げるためにはやんなきゃいけないこと多いけどみーんなはしょっちゃったよ。エラー処理も全くしてないしね。その辺頑張るよりはとっとと次のステップの構文解析、コード作成に進みたいからね。