From: kjana@... (YANAGAWA Kazuhisa) Date: 1998-12-25T23:21:32+09:00 Subject: [ruby-list:11458] Scanner generator ちょっと前に話題になったものです.parser generator はおいといて,lex もどきをつくってみました.すんごい手抜きだけど,そう悪くないかも. # 行頭に `#' がある行だけはコメントになるはずです. # 最初のモード宣言までは好きな ruby の式が書けます.まるのまま出力にコ # ピーされる. @var = nil # Scanner というクラスの # initialize になるのでそのつ # もりで. # というのが行頭にあるとモード宣言とみなします.`name' はそのま # まメソッド名になるのでちゃんとするように.必ずただ一つの # が必要です.無かったり,複数あったりするとどうなるかわかりません. # 行頭が `#' と空白文字以外の場合はその行全てがパターン. pattern # 空白文字で始まる行は pattern に対する action. expression RETURN value, :new_mode # RETURN は value を返し新たなモード(デフォルトは default)に遷移しま # す. pattern expression GOTO :default # GOTO は状態の遷移のみを行います. * マッチした文字列は matched という変数でアクセスできます. * みての通りばかばかしい程簡単な構造です. * そのため入力が期待している形に外れると激しく破綻します. シンプルというか,冗談みたい (^^; # ところで最後につけたサンプルから作った scanner は何故か余分に true # を返してくるようなのですが,何か変でしょうか? =========================================================================== 柳川和久 @ 東大阪市 . 大阪府 kjana@os.xaxon.ne.jp December 25, 1998 「くだらないダジャレで気が抜けて成仏するのではなく?」 「ではなく!!」 #!/usr/local/bin/ruby if ARGV[0] inf = File.open(ARGV[0]) ARGV.shift else inf = $stdin end if ARGV[0] out = File.open(ARGV[0], "w") ARGV.shift else out = $stdout end out.puts </ # mode declarations next if /^\s*$/ # until first mode declaration, any string is copied literally # as initialize sequence. out.puts " #{$_.chomp!}" end if $1 out.puts " end" out.puts out.puts " def #{$1}" out.puts " case @__line__" else out.puts " end" end mode = [] while inf.gets case $_.chomp! when /(?:^\s*$)|(?:^#.*)/ # ignore when /^<([_a-zA-Z]\w*)>/ # mode declarations mode << $1 out.puts " else" out.puts " c = @__line__[0]" out.puts " @__line__ = @__line__[1..-1]" out.puts " return_with_val c" out.puts " end" out.puts " end" out.puts out.puts " def #{$1}" out.puts " case @__line__" when /^\s+.*/ # actions # action strings are copied literally. out.puts " #{$_}" else # patterns out.puts " when /\\A(#{$_})/" out.puts " @__line__ = $'" out.puts " matched = $1" end end out.puts " else" out.puts " c = @__line__[0]" out.puts " @__line__ = @__line__[1..-1]" out.puts " return_with_val c" out.puts " end" out.puts " end" out.puts < [a-zA-Z_]\w* RETURN [:id, matched] \d*\.\d+(?:[eE][+-]?\d+)? RETURN [:float, matched] \d+ RETURN [:int, matched] " @part << "\"" GOTO :string \s+ # silently ignored . RETURN matched \\" @part << "\"" " @part << "\"" part = @part @part = "" RETURN [:string, part], :default [^"\\]+ @part << matched