Previous: AST Up: 目次 Next: Encoder Stage

Generator Stage

This topic has missing or partial documentation. Please help us improve it.

See How-To - Write Documentation

MelbourneはASTを作成すると、 それを入力値として次のステージ(Generator)を実行します。

ジェネレータステージではRubinius::Generatorのインスタンスが作られ、ASTの ルートにこの Generator オブジェクト上でそのバイトコード表現を生成してもらい ます。

GeneratorはRubiniusのバイトコードを生成するための純粋にRubyなDSLを提供しま す。その中心は、Rubinius Instructions に直接マップされるメソッド郡です。例えば、スタックにnilを積む命令は、 Generatorインスタンスのpush_nilメソッドを呼ぶことで作成できます。

Generatorクラスには他にも多くの便利なメソッドが定義されていて、 よくあるパターンのバイトコードはRubiniusの命令セットの 特に非常にローレベルな部分を扱わなくてもに生成できるようになっています。

例えば、直接Rubiniusのバイトコードを使ってメソッドを呼ぶには、 まずメソッド名を表すリテラルを作る必要があります。 そして、メソッドを呼ぶためにsend_stackを呼びます。 さらに、もしプライベートメソッドを実行したい場合は、 先にプライベートメソッドをの実行を特別に許可するためのバイトコードを生成します。

プライベートメソッドの実行を許可した上でputsメソッドを引数無しで呼びたい場合、 以下のように書きます:

# ここで、 g は Generator のインスタンス
g.allow_private
name = find_literal(:puts)
g.send_stack name, 0

これは、sendヘルパーを使うともっと簡単に書くことができます:

g.send :puts, 0, true

あるASTについてバイトコードを生成する際、 RubiniusはASTの各ノードについてbytecodeメソッドを呼びます。 この時、引数として現在のGeneratorインスタンスを渡します。 これは、ifノードの bytecode メソッドです:

def bytecode(g)
  pos(g)

  done = g.new_label
  else_label = g.new_label

  @condition.bytecode(g)
  g.gif else_label

  @body.bytecode(g)
  g.goto done

  else_label.set!
  @else.bytecode(g)

  done.set!
end

まず、posメソッドを呼びます。 これはNodeクラスのメソッドでg.set_line @lineを実行します。 これは、VMがデバッグ用の情報を実行時に提供するのに使われます。

次に、Generatorlabelヘルパーを使っています。 純粋な Rubiniusバイトコードは、 少数のgoto命令(gotogoto_if_truegoto_if_false)を除き、 一切の制御構造を持ちません。 goto_if_trueのかわりにgitgoto_if_falseのかわりにgif、 と短く書くことができます。 ここでは、2つの新しいラベルを作成しています。 1つはif条件の終わり、もう1つはelseブロックの開始地点を示しています。

2つのラベルが作成されると、ifノードがその子ノードである@conditionについて 現在のGeneratorオブジェクトを引数としてbytecodeメソッドを呼びます。 これにより、この条件式のバイトコードが現在のストリームに出力されます。

この処理はこの条件式の値をスタックに残すので、 ifノードはelse_labelにすぐ飛ぶためのgoto_if_false命令を出力します。 そして、上で見たのと同じやり方で@bodyのバイトコードを現在のストリームに出力したら、 条件式の最後に飛ぶための無条件のgoto命令を出力します。

次に、else_labelの場所をセットする必要があります。 ラベルの作成と使用が分離されているので、 ラベルオブジェクトの場所を指定する前にgoto命令に渡すことができます。 この性質は、いろいろな制御構造を作る際に重要になります。

それから、@elseノードのバイトコードを出力し、doneラベルの場所を指定します。

この処理はルートノードから始まり再帰的にAST全体について実行され、 結果としてGeneratorオブジェクトにASTのバイトコード表現が生成されます。

lib/compiler/astディレクトリ内に全てのASTノードとそのbytecodeメソッドが定義 されているので、参照すると役立つと思います。 Generator API の実用的な例としてもいいと思います。

GeneratorはASTのバイトコード表現を生成し終えたら、次のステージを実行します。 エンコーダステージです。

参照されているファイル

カスタマイズする

ジェネレータステージをカスタマイズする一番簡単な方法は、 Generatorにデフォルトで定義されている一般的なものに加えて、 抽象度の高いメソッドをさらに定義することです。

使われるジェネレータクラスを変更することもできます。 コンパイラの各ステージやパイプラインをカスタマイズする方法についてさらに知るには、 コンパイラパイプラインのカスタマイズ を参照して下さい。

Previous: AST Up: 目次 Next: Encoder Stage

Tweet at @rubinius on Twitter or email community@rubini.us. Please report Rubinius issues to our issue tracker.