LLVMによるプログラミング言語の実装チュートリアル日本語訳
第6章 万華鏡: 言語の拡張: ユーザ定義演算子
第4節 ユーザ定義単項演算子
現時点では万華鏡言語では、単項演算子をサポートしていないので、それをサポートするために必要なものを全てを追加する必要がある。
ただし前節で、字句解析器にunaryキーワードのサポートは追加してあるので、ここで必要になるのはASTノードの追加からである。
/// UnaryExprAST - 単項演算子のための式クラス。
class UnaryExprAST : public ExprAST {
char Opcode;
ExprAST *Operand;
public:
UnaryExprAST(char opcode, ExprAST *operand)
: Opcode(opcode), Operand(operand) {}
virtual Value *Codegen();
};
このASTノードは、実にシンプルで、今となっては分かりきったものである。
これは、子をひとつしか持たない点を除いて、二項演算子のASTノードに酷似している。
これとともに、構文解析のロジックを追加する必要がある。
単項演算子の構文解析は実に簡単である。
そのための関数を新たに追加する。
/// unary
/// ::= primary
/// ::= '!' unary
static ExprAST *ParseUnary() {
// 現在のトークンが演算子でなければ、それはプライマリ式である。
if (!isascii(CurTok) || CurTok == '(' || CurTok == ',')
return ParsePrimary();
// 単項演算子なら、読み込む。
int Opc = CurTok;
getNextToken();
if (ExprAST *Operand = ParseUnary())
return new UnaryExprAST(Opc, Operand);
return 0;
}
我々が追加した文法は実に素直である。
プライマリ式の解析中にそれが単項演算子であると判明したら、その演算子を接頭辞として取り込み、残りの部分を他の単項演算子として解析する。
これによって、多重の単項演算子(例えば、”!!x”)を処理できるようになる。
単項演算子の場合、二項演算子のときにあったような曖昧さの問題はないので、優先順位の情報は必要ない。
この関数の問題点は、この関数をどこかから呼ばなければならないということである。
そのため、以前のParsePrimaryの呼び出し元を、ParseUanryを呼び出すように変更する。
/// binoprhs
/// ::= ('+' unary)*
static ExprAST *ParseBinOpRHS(int ExprPrec, ExprAST *LHS) {
...
// 二項演算子の後の単項式を解析する。
ExprAST *RHS = ParseUnary();
if (!RHS) return 0;
...
}
/// expression
/// ::= unary binoprhs
///
static ExprAST *ParseExpression() {
ExprAST *LHS = ParseUnary();
if (!LHS) return 0;
return ParseBinOpRHS(0, LHS);
}
これら2つの簡単な変更によって、単項演算子を構文解析し、ASTを構築できるようになった。
次は、単項演算子のプロトタイプを解析するために、構文解析器に単項演算子プロトタイプのサポートを追加する必要がある。
前節の二項演算子のコードを拡張する。
/// prototype
/// ::= id '(' id* ')'
/// ::= binary LETTER number? (id, id)
/// ::= unary LETTER (id)
static PrototypeAST *ParsePrototype() {
std::string FnName;
unsigned Kind = 0; // 0 = 識別子, 1 = 単項演算子, 2 = 二項演算子。
unsigned BinaryPrecedence = 30;
switch (CurTok) {
default:
return ErrorP("Expected function name in prototype");
case tok_identifier:
FnName = IdentifierStr;
Kind = 0;
getNextToken();
break;
case tok_unary:
getNextToken();
if (!isascii(CurTok))
return ErrorP("Expected unary operator");
FnName = "unary";
FnName += (char)CurTok;
Kind = 1;
getNextToken();
break;
case tok_binary:
...
二項演算子の場合と同じように、演算子の文字を含む名前で単項演算子を名付ける。
これによって、コード生成時に使えるようになる。
あとは、単項演算子のためのコード生成機能の追加が残っている。
それは以下のようになる。
Value *UnaryExprAST::Codegen() {
Value *OperandV = Operand->Codegen();
if (OperandV == 0) return 0;
Function *F = TheModule->getFunction(std::string("unary")+Opcode);
if (F == 0)
return ErrorV("Unknown unary operator");
return Builder.CreateCall(F, OperandV, "unop");
}
このコードは、二項演算子のときのものと似ているがよりシンプルである。
特に組み込みの演算子を扱う必要がないのでよりシンプルである。
最近のコメント
たかたむ
はじめまして。初リアルフォース(R3ですが)で,同…
nokiyameego
ZFS poolのデバイスラベル破損で悩んていたと…
名前
しゅごい
Jane Doe
FYI Avoid Annoying Unexpe…
Jane Doe
ご存じとは思いますが、whileには、”~の間”と…
花粉症対策2019 – 日曜研究室
[…] 花粉症対策についてはこれまで次の記事を書いてきました。https://…
花粉症対策2019 – 日曜研究室
[…] 花粉症対策についてはこれまで次の記事を書いてきました。https://…
花粉症対策2019 – 日曜研究室
[…] 花粉症対策についてはこれまで次の記事を書いてきました。https://…
花粉症対策2019 – 日曜研究室
[…] 花粉症対策についてはこれまで次の記事を書いてきました。https://…
花粉症対策2019 – 日曜研究室
[…] 花粉症対策についてはこれまで次の記事を書いてきました。https://…