ミニキャン言語で負数に対応した話

はじめに

ミニキャン言語で発展課題である負数の実装ができたのでアウトプットします。
兎にも角にもコードだという人は以下を参照してください。

github.com

実装

今回負数の実装をしたのですが、最初は定数のみ(-1や-2など)にしか対応しておらず、その後変数/引数(-xなど)に対応させました。
なので、最初に定数のみの実装について書いた後に引数に対応した処理を書きます。

負の定数の実装

定数のみの実装はこんな感じです。

static std::unique_ptr<ExprAST> ParseNegNumberExpr() {
    getNextToken();
    if (CurTok != tok_number) {
        return LogError("expected number after '-'");
    } else {
        auto Result = llvm::make_unique<NumberAST>(-lexer.getNumVal());
        getNextToken();
        return std::move(Result);
    }
}

これは他のミニキャン参加者の実装を真似させていただきました。
要となっている処理は以下の処理です。

auto Result = llvm::make_unique<NumberAST>(-lexer.getNumVal());

NumberASTにセットする値を読み込んできた際に、その場で負数の扱いにしてASTを作るという処理です。
この処理をどこで呼び出すのかというとParsePrimaryで呼び出します。

case '-':
    return ParseNegNumberExpr();

上記のcase文を追加することで負数のParseを行います。
ParseのタイミングはNumberAST(正数)と同じで良いでのここで呼び出します。

で、ここまでで負の定数なら処理することができるようになりました。
なので今の状態なら1 + -1などが計算できます。
しかし、1 + -xは計算できませんので、そこに対応していきます。

負の変数の実装

負数の実装は色々方法があるかもしれませんが私は0 - xという処理をすることで負数にするという方法にしました。
前半の実装ではそのままNumberASTを使っていましたが今回は別にNegNumberASTというのを用意しました。

    class NegNumberAST : public ExprAST {
        std::unique_ptr<ExprAST> LHS, RHS;

        public:
        NegNumberAST(std::unique_ptr<ExprAST> LHS,
                std::unique_ptr<ExprAST> RHS)
            : LHS(std::move(LHS)), RHS(std::move(RHS)) {}

        Value *codegen() override;
    };

LHSは0で固定なのですがcodegenの実装が楽になると思いフィールドを用意しました。
主に使うのはRHSです。
そして重要なParseの部分です。

static std::unique_ptr<ExprAST> ParseNegNumberExpr() {
    getNextToken();
    auto RHS = ParsePrimary();
    auto LHS = llvm::make_unique<NumberAST>(0);
    return llvm::make_unique<NegNumberAST>(std::move(LHS), std::move(RHS));
}

ParseNegNumberを読む時のCurTokは-なのでTokenを進めます。
RHSはその後の数字や識別子をParseしたものを保持します。
LHSは0 - xなので0のNumberASTを作成します。

codegenは以下のようになります。

Value *NegNumberAST::codegen() {
    Value *L = LHS->codegen();
    Value *R = RHS->codegen();
    if (!L || !R)
        return nullptr;

    return Builder.CreateSub(L, R, "subtmp");
}

LHSとRHSをそれぞれcodegenしてからLHS - RHSの式をcodegenします。
これはBinaryASTのcodegenをそのまま真似した式です。

これでようやく実装できましたので、処理結果を載せます。
f:id:kisaragi211:20191203193121p:plain f:id:kisaragi211:20191203193136p:plain
二つ目の画像で0 - xの中間コードが出ているのがわかります。

まとめ

負数の実装は時間はかかりましたが、想像より簡単な処理で行うことができました。
BinaryASTのcodegenやParseBinOpRHSを真似すれば誰でも実装できそうだなという感想です。

アドバイス等ございましたらツイッター等でメッセージをください。