コンパイラ作成(20) コメント
今回の目標
式の解析に飽きたんで、いきなりだけどコメント機能を追加することにしたよ。
// コメントだよ main() { return 42; // ここもコメント }
C++スタイルの一行コメント。
C言語のコメント
C言語のコメントは本来/* ~~~*/の形式だけど、mycではサポートしないことにしたよ。理由は面倒くさいから。明快な理由だろ。あとネストした場合のいやらしい問題もあるしね。
C++スタイルの//の形式のコメントは今じゃ正式にC言語の規約に入ってるんだね。Wikipedia先生によるとC99からみたい。まあ、その前から多くのコンパイラがサポートしてたよね。
Lexerクラスのモジュールテスト
コメント機能を付け加える前にLexerクラスの単体テスト機能を追加することにした。前々からやろうと思ってたんだよね。
if ARGV[0] == "-l" then fname = ARGV[1] lex = Lexer.new(fname) lex.moduletest lex.close exit -1 end
Lexerクラスにテスト用のメソッドを追加して、それを呼ぶようにしたよ。中身は最初に作ったやつと同じ。
Rubyにもgetoptみたいなやつあるんだろうけど、今回は適当なコーディングにしちゃった。(毎回、超適当だろ)
Lexerクラス
それでは本題のコメント機能を追加するんで、Lexerクラスのgettokenに手を入れるよ。えーと、どうなってたっけ?Lexerクラスのコーディングしたのは遥か昔の出来事なんですっかり忘れてる。
# 空行、空白、コメントをスキップする loop do if @line == nil then return TK::EOF, str end @line.chomp! # 改行文字を削除 if @line[@idx] then loop do if !@line[@idx] then break elsif @line[@idx].match(/\p{ascii}/) && !@line[@idx].match(/\s/) then break else @idx += 1 end end if @line[@idx] == '/' && @line[@idx+1] == '/' then @idx = @line.length end if @line[@idx] then break end end @line = @io.gets @lineno += 1 @idx = 0 end
一行追加した。思い出すのに10分、コーディングに30秒かかったよ。
動作テスト
上手くいくかな?
~/myc$ myc -l j.myc TK::ID main TK::SYMBOL ( TK::SYMBOL ) TK::SYMBOL { TK::ID return TK::NUMBER 42 TK::SYMBOL ; TK::SYMBOL } ~/myc$
できた!ちゃんとコメント部分がスキップされてる。意外と簡単に実現できたよ。式の解析とは大違いだ。さて次は何やろうかな。
ちなみに今のmyc.rbは480行。500行弱で割と色々できるもんだなあ。