コンパイラ作成(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行弱で割と色々できるもんだなあ。