コンパイラ作成(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  ;

動かしてみたけどちゃんと動いてるみたい。今回作った分だとサポートしてる演算子は+、-、=だけ。文字列リテラル中のエスケープシーケンスの処理も入ってないなあ。コメントのスキップ機能とかちゃんとしたコンパイラに仕上げるためにはやんなきゃいけないこと多いけどみーんなはしょっちゃったよ。エラー処理も全くしてないしね。その辺頑張るよりはとっとと次のステップの構文解析、コード作成に進みたいからね。