NODE_WHILE など
続いて、ループ構文です。
while 条件式 本体 end
のような形のwhile式をコンパイルします。untilは、whileと終了条件の真偽が逆になったバージョンで、NODE_OPT_Nは、rubyコマンドを-nオプションで起動したときに自動で作られるループ構造
while gets スクリプト本体 end
に対応するノードです。どれも同じことをやってるだけなので、コンパイル処理は1カ所にまとまっています。
あと、後置のwhile/until(while/until修飾子)
print while gets
などは、構文解析の段階で普通のwhile文と同じ構文木に直されているので、YARVでは考えません。
本体のコンパイルにはおなじみの COMPILE_ マクロ、条件判定のコンパイルは if のところでやった compile_branch_condition が使われています。あとはいつもどおりのADD_INSNとADD_LABELの列で、特に新しい部分はないのでコンパイラのコードは省略しちゃいます。生成されるマシン語列は、以下の順番になります。
jmp next_label redo_label: 本体 next_label: 条件判定 end_label: putnil break_label: if(poped) pop
Rubyのループ制御文である redo, next, break で飛べるジャンプ先に、それぞれラベルがついています。普通にループを抜けると全体の評価値はnilですが、breakで抜けるとbreakに指定された値が評価値となります。このため、end_labelとbreak_labelが完全に同じ位置にはなりません。
おなじみのコード生成のあと、最後はこんなコードで終わっています。
case NODE_WHILE: { ... (略) ... ADD_CATCH_ENTRY(CATCH_TYPE_BREAK, redo_label, break_label, 0, break_label); ADD_CATCH_ENTRY(CATCH_TYPE_NEXT | 0x10000, redo_label, break_label, 0, iseq->compile_data->start_label); ADD_CATCH_ENTRY(CATCH_TYPE_REDO, redo_label, break_label, 0, iseq->compile_data->redo_label); iseq->compile_data->start_label = prev_start_label; iseq->compile_data->end_label = prev_end_label; iseq->compile_data->redo_label = prev_redo_label; iseq->compile_data->loopval_popped = prev_loopval_popped; iseq->compile_data->ensure_node_stack = enlp; break; }
さて、これはなんでしょう?
...というところで時間が無くなったので次回に続きます。