技術

7.6. 新しい代入演算子

LLVMによるプログラミング言語の実装チュートリアル日本語訳
第7章 万華鏡: 言語の拡張: 変更可能な変数(Mutable Variables)
第6節 新しい代入演算子

我々の現在のフレームワークに、代入演算子を新しく追加するのは実に簡単である。
代入演算子を他の二項演算子と同じように解析するが、処理は内部的に行う。(ユーザ定義不可。)
最初の一歩は、優先順位を設定するところからである。

int main() {
  // 標準の二項演算子のインストール。
  // 1が一番低い優先順位となる。
  BinopPrecedence['='] = 2;
  BinopPrecedence['<'] = 10;
  BinopPrecedence['+'] = 20;
  BinopPrecedence['-'] = 20;

これで、その二項演算子の優先順位を構文解析器が知っている状態になり、構文解析とAST生成時に面倒を見てくれる。
我々に必要なのは、代入演算子のためのコード生成処理の実装だけである。
それは以下のようになる。

Value *BinaryExprAST::Codegen() {
  // LHSを式として生成したくないので、"=”は特殊なケースとなる。
  if (Op == '=') {
    // 代入はLHSが識別子である必要がある。
    VariableExprAST *LHSE = dynamic_cast<VariableExprAST*>(LHS);
    if (!LHSE)
      return ErrorV("destination of '=' must be a variable");

他の二項演算子とは違って、代入演算子は“LHSを生成し、RHSを生成し、計算を行う(emit LHS, emit RHS, do computation)”というモデルには従わない。
なので、他の二項演算子が処理される前に、特殊なケースとして代入演算子は処理される。
他には、代入演算子はLHSが変数であることを要求する点が違う。
“(x+1) = expr”は無効であり、“x = expr”のような場合だけが許可される。

  // RHSのコード生成。
  Value *Val = RHS->Codegen();
  if (Val == 0) return 0;

  // 名前を探す。
  Value *Variable = NamedValues[LHSE->getName()];
  if (Variable == 0) return ErrorV("Unknown variable name");

  Builder.CreateStore(Val, Variable);
  return Val;
}
...

変数を取得したら、代入のコード生成はまるっきりそのまんまである。
代入に必要なRHSのコード生成を行い、store命令を生成し、計算された値を返す。
この値を戻す事によって、“X = (Y = Z)”のような連鎖代入が可能となる。

これで代入演算子を得たことになり、関数の引数やループ変数を変化させることが可能となった。
例えば、現時点では以下のようなコードを実行出来る。

# doubleの値を印字する関数。
extern printd(x);

# 順次演算子。オペランドを無視しRHSを返す、低優先順位の演算子":"を定義する。
def binary : 1 (x y) y;

def test(x)
  printd(x) :
  x = 4 :
  printd(x);

test(123);

これを実行すると、”123″のあとに”4″が印字され、実際に値を変化させれることが分かる。
これで我々は、本格的に我々のゴール(代入は、一般的な場合においてSSA構築を要求する)を達成した。
しかしながら、より便利にするために、ローカル変数を定義出来るようにしたい。
次にこれを追加してみよう!

コメントを残す

メールアドレスが公開されることはありません。



※画像をクリックして別の画像を表示

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください