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"); }
このコードは、二項演算子のときのものと似ているがよりシンプルである。
特に組み込みの演算子を扱う必要がないのでよりシンプルである。
最近のコメント
名前
しゅごい
Jane Doe
FYI Avoid Annoying Unexpe…
Jane Doe
ご存じとは思いますが、whileには、”~の間”と…
peta_okechan
針金みたいなパーツを引っ張ると外れます。 他の方の…
虎徹ファン交換
虎徹の標準ファンを外す際に、どのようにして外されま…
花粉症対策2019 – 日曜研究室
[…] 花粉症対策についてはこれまで次の記事を書いてきました。https://…
花粉症対策2019 – 日曜研究室
[…] 花粉症対策についてはこれまで次の記事を書いてきました。https://…
花粉症対策2019 – 日曜研究室
[…] 花粉症対策についてはこれまで次の記事を書いてきました。https://…
花粉症対策2019 – 日曜研究室
[…] 花粉症対策についてはこれまで次の記事を書いてきました。https://…
花粉症対策2019 – 日曜研究室
[…] 花粉症対策についてはこれまで次の記事を書いてきました。https://…