コンパイラ作成(3) Lexerクラス
# 字句解析 class Lexer # コンストラクタ def initialize(fname) @fname = fname # ソースファイルのファイル名 begin @io = File.open(@fname,"r") rescue SystemCallError => e puts %Q(class=[#{e.class}] message=[#{e.message}]) end # 1行先読み @line = @io.gets @idx = 0 end # ファイルクローズ def close @io.close end # テストメソッド def pfname puts "fname="+@fname end # テストメソッド def cat loop do if @line == nil then break end @line.chomp! # 改行文字を削除 puts @line @line = @io.gets end end # トークンを一つ切り出す # kind, str = gettoken def gettoken str = "" # 空行、空白をスキップする 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] then break end end @line = @io.gets @idx = 0 end # tokenを切り出す if @line[@idx].match(/\p{ascii}/) then # identifireの切り出し if @line[@idx].match(/\p{alpha}/) then str += @line[@idx] @idx += 1 loop do if !@line[@idx] then break end if !@line[@idx].match(/\p{ascii}/) then break end if !@line[@idx].match(/\p{alnum}/) then break end str += @line[@idx] @idx += 1 end return TK::ID, str # numberの切り出し elsif @line[@idx].match(/\p{digit}/) then str += @line[@idx] @idx += 1 loop do if !@line[@idx] then break end if !@line[@idx].match(/\p{ascii}/) then break end if !@line[@idx].match(/\p{digit}/) then break end str += @line[@idx] @idx += 1 end return TK::NUMBER, str # stringの切り出し elsif @line[@idx] == "\"" then @idx += 1 loop do # puts @line[@idx] if !@line[@idx] then break end if @line[@idx] == "\"" then @idx += 1 break end str += @line[@idx] @idx += 1 end return TK::STRING, str # operatorの切り出し elsif @line[@idx].match(/=|\+|\-|;|\(|\)|\{|\}/) then str += @line[@idx] @idx += 1 return TK::SYMBOL, str end end # tokenとして解釈不可能な文字 str += @line[@idx] @idx += 1 return TK::UNKNOWN, str end end
ところどころコメント間違ってたりするなあ。あれもしかしてidentifireじゃなくてidentifierじゃね。こっぱずかしい。はてなブログのエディタに指摘されて気付いたよ。ちゃんと直しとこう。