「Hello world! 言語」再び

前記、スキャナを別メソッドにする、そしてその辺の呼び出しは yyparse を使う、@queue のようなものは必要なくなった。initializeメソッドで @sourceインスタンス変数にソースを格納するようにしてみる。あと、デバッグオプションは OptionParser と OpenStruct を使う。OptionParserのメッセージ類も少し書く。
「---- header」セクションも作ってバージョンなど。それからマジックコメントもここに書いておく、しかし実行ファイルの本当の先頭にコピーされるわけではないので余り意味はない。

class HParser
rule# class HParser

  program :
          | program 'H' {puts "Hello world!\n"}

end # class HParser

---- inner
def initialize(source)
  @source = source
end # def initialize(source)

def scan
  @source.each_char{ |h| yield [h, nil] if 'H' == h }
  yield nil                                                 # (A)
end # def scan

def parse(opts = nil)
  @yydebug = opts.yydebug if opts.respond_to? :yydebug
  yyparse self, :scan
end # def parse(opts = nil)

---- header
# -*- coding: utf-8; -*-
Version = '$Id: Hhyy.y 3236 2009-01-31 06:00:02Z hs9587 $'
# $Date: 2009-01-31 15:00:02 +0900 (土, 31 1 2009) $

---- footer
require 'optparse'
require 'ostruct'
opts = OpenStruct.new
opts.yydebug = false

ARGV.options do |opt|
  opt.on('--[no-]yydebug', 'debug mode (from racc -t) on/off'){ |v| opts.yydebug = v }
  opt.release = '0.1'
  opt.banner += "\n\tH the 'Hello world!' interpreter.\nOptions:"
  opt.parse!
end # ARGV.options do |opt|

HParser.new((ARGV.size > 0 ? ARGF : $stdin).read).parse(opts)

入力の終わり、next_tokenメソッドを使っていたときは @queue が最後に nil を返してくれたが、今の場合は明示的に nil を yield してやる (A)。