人気ブログランキング | 話題のタグを見る

irb その10 @scanner.each_top_level_statement

横道はこれくらいにして、いよいよ irb > eval_input の総本山と思える @scanner.each_top_level_statement メソッドをのぞいてみよう。eval_input のこの部分の最初の部分は次のようになる。

  @scanner.each_top_level_statement do |line, line_no|
    signal_status(:IN_EVAL) do
      begin
        line.untaint
        @context.evaluate(line, line_no)
        output_value if @context.echo?
      rescue StandardError, ScriptError, Abort
        .......

RubyLex#each_top_level_statement メソッドはコードブロックを引数にとる高階関数だ。したがって、上のコードでは do |line, line_no| ... 以下のコードブロックがこのメソッドの引数として渡される。do ... end は次のように、{|line, line_no| ... } とも書けるので each_top_level_statement の引数としてみるときは、こっちの表示の方が直観的にわかりやすい。

さてこのコードブロックを見てみよう。

最初は signal_status(:IN_EVAL) do ... end となっているが、これは引数のコードブロックを実行するときに一時的にそのシグナル状態を :IN_EVAL に変更するという関数なので当座は無視することにする。

そうすると、本質的な部分は begin 以下の部分になるが、なんと begin 以下4行目は rescue になっている。つまりこの長いコードブロックの実質的な機能は3行だけであとはすべてエラー処理にあてられているのだ。

そこで、この3行が何をしているかというと、引数の line (おそらくコンソールから入力したRubyの文が入っていると思われる。)を untaint して、@context.evaluate(line, line_no) メソッドで line に代入されている文を実行し、その結果を output_value メソッドで表示している。

line や line_no の情報は each_top_level_statement メソッドが持っていて、このコードブロックを使って line を実行するのだろう。

なぜこのようなスタイルにしたのかはわからないが、しかし、each_top_level_statement メソッドに、{|line, line_no| print line_no, " : ", line, "\n"} のようなコードブロックを引数として与えると、line は実行されず、行番号とともに表示されることになる。

ともあれ、irb のプロンプトから入力された Ruby の文がどこで実行されているのかが分かった。

ところで、文を一行実行させるだけなら次のような micro_irb.rb でも十分だ。

while true
  print '> '
  p eval(gets)
end

しかし、これでは、複数行の文からなる制御構造は扱えない。それらを扱うためにはどうしても部分的な構文解析が必要なのだ。それをやっているのが irb の処理なのだろう。

micro_irb.rb だが、これくらい小さいと、次のように一行ですませることができる。

> ruby -e "loop {print '> '; p eval(gets)}"
by tnomura9 | 2008-04-04 23:22 | Ruby | Comments(0)
<< irb その11 RubyLe... irb その9 機能縮小版 b... >>