# mc-lang-02 note

mc-lang-02 (opens new window)のメモです。

# 2.1 識別子をトークナイズする

識別子とは, 変数名や関数名のこと。

def hoge()で考えてみる。 この場合, defおよびhogeが識別子に該当する。

これらの識別子を認識するためには, 1文字目から見ていくのが良さそうである。文字列を順に記憶して, スペース等の別の記号が来たら識別子として登録すれば良い。

以下めも。

  • 実装はlexer.h
  • アルファベットか否かはisalpha() (opens new window)で確認できる
    • 大文字小文字のa-zならば非ゼロ, そうでなければゼロを返す
  • getNextChar(iFile)は次の文字を1つ読み込む
  • setIdentifier(hoge)でidentifierStrにhogeをセットする

# 2.2 識別子をパースしよう

トークンが識別子の場合は、引数(変数)の参照か関数の呼び出しの為、引数の参照である場合はVariableExprASTを返し、関数呼び出しの場合はCallExprASTを返す。

VariableExprAST (opens new window)は, 変数の名前を表すクラスである。そして, CallExprAST (opens new window)は関数呼び出しを表すクラスである。

トークンが識別子の場合, 2.1で実装した"def"かそれ以外の文字列(hoge等の他の文字列)になっているはずである。識別子が前者か後者かで判断すれば良い。

def hoge(x y)で考えてみる。 この場合, まず識別子として"def"が来るので, 関数呼び出しと認識する。次に識別子として"hoge"が来るので, 変数(関数名)として認識される。そして"("が認識され, 続く"x", "y"も同様に変数として認識される。最後に")"が認識されてこの行のパースが終了する。

以下めも。

  • 実装はparse.h
  • hoge, fuga: 変数
  • def hoge(fuga): 関数(def)
  • Lexer lexermc.cで既に宣言されている (opens new window)ので, getIdentifierは単にlexer.getIdentifier()で呼び出せる
  • Hogeクラスの新しいインスタンスのunique_ptrを作りたい場合, llvm::make_unique< Hoge >(fuga)のようにして生成できる
    • fugaはコンストラクタに渡す引数
  • unique_ptrはスマートポインタと呼ばれ, メモリアクセスと解放をするための所有権を1つのオブジェクトのみに許すポインタである
    • スマートポインタは, デストラクタで自動的にdeleteしてくれるので, メモリの漏洩がなく安全なメモリ管理ができる

# 2-3 関数のシグネチャをパースしよう

2.2とほぼ同じ。CallExprASTではなくPrototypeASTを返し、引数同士の区切りが','ではなくgetNextToken()を呼ぶと直ぐにCurTokに次の引数(もしくは')')が入るという違いのみ。

先ほどの2-2は, 関数内部(body)部分のパースであった。それに対し, この2-13では関数のシグネチャ(関数名, 関数の引数といった関数の情報)をパースする。

def hoge(x y)で考えてみる。 まず, "def"はlexer.getIdentifier()tok_defに変換されているのでスルーする。次に, 識別子"hoge"がきて, 関数名(Name)が認識される。そして, '('が認識され, 続く識別子"x", "y"が引数であることが認識される。最後に, ')'が認識されて, この行のパースが終了する。

以下めも

  • 実装はparse.h
  • std::vectorが保持するものが異なる
    • こちらのstd::vectorはstd::stringを保持する

# 2-4 引数のcodegenを実装してみよう

NamedValuesの中にVariableExprAST::NameとマッチするValueがあるかチェックし、あったらそのValueを返す。

以下めも

  • 実装はcodegen.h
  • std::mapはそのままアクセスすると, '0'が生成されてしまうのでそのままアクセスするのはNG

# 2-5 関数呼び出しのcodegenを実装してみよう

関数呼び出し元のcodegen()を実装する。2. でllvm::Function::arg_sizeを比較しているのは, オーバーロードされていた時に区別をつけるため。関数名および引数を用いて関数呼び出しに該当するBuilder.CreateCall()を呼び出す。

以下めも

# 2-6 C++を用いてELFファイルを作り, 実行してみよう

以下のコマンドを実行するだけ

./mc test/test4.mc
clang++-8 main.cpp output.o -o main
./main
Last Updated: 9ヶ月前