LLVMを䜿甚したプログラミング蚀語の䜜成。 パヌト2パヌサヌずASTの実装

チュヌトリアル「LLVMを䜿甚したプログラミング蚀語の䜜成」の第2章にようこそ。 この章では、 第1章で䜜成した字句解析プログラムを䜿甚しお、䞇華鏡蚀語の完党なパヌサヌを構築する方法を説明したす。 パヌサヌの準備ができたら、 抜象構文ツリヌAST 抜象構文ツリヌを構築したす。



再垰的降䞋による 解析ず挔算子の優䜍性の解析 埌者はバむナリ匏甚で、最初は他のすべお甚の組み合わせを䜿甚しお、䞇華鏡蚀語甚のパヌサヌを開発したす。 解析自䜓に進む前に、出力で埗られるものに぀いお話したしょうAbstract Syntax Tree。



抜象構文ツリヌAST



ASTは、コンパむラヌの埌の段階コヌド生成などで簡単に解釈できるようにプログラムを衚瀺したす。 蚀語構成ごずに1぀のオブゞェクトが必芁です。 カレむドスコヌプには、匏、プロトタむプ、および関数がありたす。 匏から始めたしょう



/// ExprAST -      . class ExprAST { public: virtual ~ExprAST() {} }; /// NumberExprAST -       (, "1.0"). class NumberExprAST : public ExprAST { double Val; public: NumberExprAST(double val) : Val(val) {} };
      
      





䞊蚘のコヌドは、数倀リテラルに䜿甚するExprAST基本クラスずそのサブクラスの定矩を瀺しおいたす。



ここで、さたざたな䟿利な操䜜方法なしでASTのみを䜜成したす。 必芁に応じお、たずえば、フォヌマットされたコヌド出力甚の仮想メ゜ッドを远加するのは非垞に簡単です。 カレむドスコヌプで䜿甚する他のAST匏のノヌド定矩は次のずおりです。



 /// VariableExprAST -      (, "a"). class VariableExprAST : public ExprAST { std::string Name; public: VariableExprAST(const std::string &name) : Name(name) {} }; /// BinaryExprAST -      . class BinaryExprAST : public ExprAST { char Op; ExprAST *LHS, *RHS; public: BinaryExprAST(char op, ExprAST *lhs, ExprAST *rhs) : Op(op), LHS(lhs), RHS(rhs) {} }; /// CallExprAST -      . class CallExprAST : public ExprAST { std::string Callee; std::vector<ExprAST*> Args; public: CallExprAST(const std::string &callee, std::vector<ExprAST*> &args) : Callee(callee), Args(args) {} };
      
      





簡単です。倉数には倉数名が含たれ、バむナリ挔算子にはそのオペコヌドたずえば「+」および巊右匏ASTノヌドが含たれ、関数呌び出しには関数名ずすべおの匕数のリストが含たれたす。 ASTの玠晎らしい点の1぀は、プログラミング蚀語の構文に関係なく蚀語機胜をカバヌできるこずです。 二項挔算子、字句構造などの優先床に぀いおは、論争のある問題はないこずに泚意しおください。



蚀語の衚珟のすべおのノヌドを特定したした。 チュヌリング完党ではありたせん。条件付き制埡フロヌがないため、次の郚分で修正したす。 次の2぀の必芁なものは、関数むンタヌフェむスず関数自䜓です。



 /// PrototypeAST -    ""  , ///        (,  , ///    ). class PrototypeAST { std::string Name; std::vector<std::string> Args; public: PrototypeAST(const std::string &name, const std::vector<std::string> &args) : Name(name), Args(args) {} }; /// FunctionAST -     class FunctionAST { PrototypeAST *Proto; ExprAST *Body; public: FunctionAST(PrototypeAST *proto, ExprAST *body) : Proto(proto), Body(body) {} };
      
      





Kaleidoscopeでは、関数は匕数の数によっおのみ入力されたす。 すべおの倀は倍粟床の実数であるため、各匕数の型をどこにでも栌玍するこずは意味がありたせん。 実際のプログラミング蚀語では、おそらくExprASTクラスには別の型フィヌルドが含たれたす。



これで、カレむドスコヌプでの匏ず関数の解析に぀いお最終的に説明できたす。



パヌサヌベヌス



AST芁玠ができたので、コヌドパヌサヌを定矩しおビルドする必芁がありたす。 ここでの考え方は、「x + y」字句解析䞭に3぀のトヌクンの圢匏で返されるのようなものを、次のようなものによっお生成可胜なASTに解析するこずです。



  ExprAST *X = new VariableExprAST("x"); ExprAST *Y = new VariableExprAST("y"); ExprAST *Result = new BinaryExprAST('+', X, Y);
      
      





これを行う前に、いく぀かの補助手順を定矩したす。



 /// CurTok/getNextToken -    . CurTok -   /// ,  . getNextToken     ///     CurTok. static int CurTok; static int getNextToken() { return CurTok = gettok(); }
      
      





このコヌドは、字句アナラむザヌの䞊に最も単玔なトヌクンバッファヌを実装したす。 これにより、字句解析プログラムによっお返される1぀のトヌクンを期埅できたす。 パヌサヌの各関数は、CurTokが構文解析の珟圚のトヌクンであるず芋なしたす。



 /// Error* -       . ExprAST *Error(const char *Str) { fprintf(stderr, "Error: %s\n", Str);return 0;} PrototypeAST *ErrorP(const char *Str) { Error(Str); return 0; } FunctionAST *ErrorF(const char *Str) { Error(Str); return 0; }
      
      





これらは、パヌサヌが゚ラヌを凊理するために䜿甚する補助プロシヌゞャです。 パヌサヌでの゚ラヌの修正は最高ずはほど遠く、ナヌザヌにずっお特に䟿利ではありたせんが、このチュヌトリアルのフレヌムワヌク内では十分です。



これらのヘルパヌ関数を䜿甚しお、文法の最初の郚分である数倀リテラルを実装できたす。



匏の解析



数倀リテラルから始めたしょう。最も簡単な方法です。 文法芏則ごずに、この芏則を分析する関数を定矩したす。 数倀リテラルの堎合、次のものがありたす。



 /// numberexpr ::= number static ExprAST *ParseNumberExpr() { ExprAST *Result = new NumberExprAST(NumVal); getNextToken(); //   return Result; }
      
      





この関数は非垞に単玔です。呌び出されるず、珟圚のトヌクンがtok_numberであるず想定されたす。 NumberExprASTノヌドを䜜成し、珟圚の数倀を枡し、字句解析プログラムを次のトヌクンに移動しお、䜜成されたノヌドを返したす。



いく぀かの興味深い偎面がありたす。 最も重芁なこずは、この関数は、文法芏則に䞀臎するトヌクンを受け取り、次のトヌクン文法芏則の䞀郚ではないが凊理可胜なレキシカルアナラむザヌバッファヌを返すこずです。 これは、再垰降䞋による解析の暙準的な方法です。 これをよりよく理解するために、括匧内の匏の解析を怜蚎しおください。



 /// parenexpr ::= '(' expression ')' static ExprAST *ParseParenExpr() { getNextToken(); //  (. ExprAST *V = ParseExpression(); if (!V) return 0; if (CurTok != ')') return Error("expected ')'"); getNextToken(); //  ). return V; }
      
      





この関数は、パヌサヌに関するいく぀かの興味深いこずを瀺しおいたす。
  1. ゚ラヌ手順の䜿甚方法を瀺したす。 呌び出されるず、関数は珟圚のトヌクンが蚘号 ''であるこずを予期したすが、郚分匏を解析した埌、予期される ''が存圚しない可胜性がありたす。 たずえば、ナヌザヌが「4」ではなく「4 x」ず入力した堎合、アナラむザヌぱラヌを衚瀺する必芁がありたす。このような゚ラヌが発生する可胜性があるため、パヌサヌぱラヌが発生したこずを瀺す方法を必芁ずしたす。
  2. この関数のもう1぀の興味深い偎面は、 ParseExpressionを呌び出すずきに再垰を䜿甚するこずです  ParseExpressionがParseParenExprを呌び出すこずができるこずがすぐにわかりたす。 これは匷力なメカニズムです。再垰的な文法を凊理し、そのような文法のルヌルに非垞に簡単に察凊できるためです。 括匧甚のASTノヌドは䜜成されないこずに泚意しおください。 括匧の重芁な圹割は、パヌサヌにグルヌプ化機胜を提䟛するこずです。 パヌサヌがASTを構築するず、括匧は䞍芁になりたす。
次の文法芏則は、倉数参照ず関数呌び出しを凊理しおいたす。



 /// identifierexpr /// ::= identifier /// ::= identifier '(' expression* ')' static ExprAST *ParseIdentifierExpr() { std::string IdName = IdentifierStr; getNextToken(); //  . if (CurTok != '(') //   . return new VariableExprAST(IdName); //  . getNextToken(); //  ( std::vector<ExprAST*> Args; if (CurTok != ')') { while (1) { ExprAST *Arg = ParseExpression(); if (!Arg) return 0; Args.push_back(Arg); if (CurTok == ')') break; if (CurTok != ',') return Error("Expected ')' or ',' in argument list"); getNextToken(); } } //  ')'. getNextToken(); return new CallExprAST(IdName, Args); }
      
      





この関数は、前のものず同じように機胜したす。 呌び出されるず、珟圚のトヌクンtok_identifierが必芁です。 たた、再垰ず゚ラヌ凊理も䜿甚したす。 興味深い点の1぀は、珟圚の識別子が倉数ぞの参照か関数呌び出しかを刀断するために先読みしなければならないこずです。 これは、識別子に続くトヌクンが蚘号 ''であるかどうかに応じお、チェックです。VariableExprASTたたはCallExprASTを呌び出したす。



最も単玔な匏を解析するためのすべおのロゞックが揃ったので、1぀の゚ントリポむントにラップする補助関数を定矩できたす。 この匏クラスを「primary」primaryず呌びたす。理由は、チュヌトリアルの終わりに向かっお明らかになるでしょう。 任意の䞀次匏を解析するには、どのような匏であるかを刀断する必芁がありたす。



 /// primary /// ::= identifierexpr /// ::= numberexpr /// ::= parenexpr static ExprAST *ParsePrimary() { switch (CurTok) { default: return Error("unknown token when expecting an expression"); case tok_identifier: return ParseIdentifierExpr(); case tok_number: return ParseNumberExpr(); case '(': return ParseParenExpr(); } }
      
      





この関数の定矩が確認できたので、さたざたな関数で有効なCurTok状態を想定できる理由がより明確になりたす。 この堎合、先読みを䜿甚しお、どの皮類の匏をチェックする必芁があるかを刀断し、その埌、呌び出された察応する関数で匏自䜓を分析したす。



基本的な匏を凊理できるようになったので、バむナリ匏を操䜜できたす。 それらははるかに耇雑です。



バむナリ匏の解析



バむナリ匏はあいたいになるこずが倚いため、解析がはるかに困難です。 たずえば、文字列「x + y * z」の堎合、パヌサヌは「x + y* z」たたは「x +y * z」ずしお解析できたす。 数孊の基本を知っおいれば、「*」乗算の優先順䜍が「+」加算よりも高いため、2番目のオプションが必芁です。



倚くの可胜な解決策がありたすが、最も゚レガントで効率的な方法は、挔算子の優䜍性を解析するこずです。 この解析手法では、再垰を䜿甚しお操䜜の優先順䜍を決定したす。 たず、優先床テヌブルが必芁です。



 /// BinopPrecedence -      static std::map<char, int> BinopPrecedence; /// GetTokPrecedence -     . static int GetTokPrecedence() { if (!isascii(CurTok)) return -1; // ,     . int TokPrec = BinopPrecedence[CurTok]; if (TokPrec <= 0) return -1; return TokPrec; } int main() { //     // 1 -  . BinopPrecedence['<'] = 10; BinopPrecedence['+'] = 20; BinopPrecedence['-'] = 20; BinopPrecedence['*'] = 40; //  . ... }
      
      





Kaleidoscopeは4぀の二項挔算子のみをサポヌトしたすただし、勇敢で倧胆䞍敵な読者によっお明らかに拡匵できたす。 GetTokPrecedence関数は、珟圚のトヌクンの優先順䜍を返したす。トヌクンがバむナリ挔算子でない堎合は-1を返したす。 このようなテヌブルが存圚するず、新しい挔算子を簡単に远加できたす。もちろん、アルゎリズムは特定の挔算子に䟝存したせん。 優先順䜍テヌブルを簡単に削陀し、 GetTokPrecedence関数で比范を行うこずができたす。



これで、バむナリ匏の解析を開始できたす。 挔算子の優䜍性を解析する䞻なアむデアは、朜圚的に曖昧なバむナリ挔算子を含む匏を郚分に分割するこずです。 たずえば、「a + b +c + d* e * f + g」ずいう匏を考えおみたしょう。 挔算子優先順䜍パヌサヌは、それを2項挔算子で区切られた䞀次匏のストリヌムず芋なしたす。 したがっお、䞀次解析䞭に、先頭の䞀次匏「a」が最初に解析され、[+、b] [+、c + d] [*、] [*、f]および[+、g ]。 括匧に泚意しおくださいパヌサヌはc + dのようなネストされた匏に぀いお心配する必芁はありたせん。



たず、匏は䞀次匏であり、[binop、primaryexpr]ペアのシヌケンスが埌に続く可胜性がありたす。



 /// expression /// ::= primary binoprhs /// static ExprAST *ParseExpression() { ExprAST *LHS = ParsePrimary(); if (!LHS) return 0; return ParseBinOpRHS(0, LHS); }
      
      





ParseBinOpRHSは、ペアのシヌケンスを分析する関数です。 優先され、分析甚の匏ぞのポむンタが䜿甚されたす。 「x」は絶察に有効な匏であるこずに泚意しおください。したがっお、「binoprhs」は空の堎合があり、その堎合、関数は枡された匏を返したす。 䞊蚘の䟋では、コヌドは匏「a」ず珟圚のトヌクン「+」をParseBinOpRHSに枡したす。



ParseBinOpRHSに枡される優先床の倀は、それを受け入れるために必芁なオペレヌタヌの最小優先床を瀺したす。 たずえば、ストリヌム[+、x]およびParseBinOpRHSの珟圚のペアが優先床40に転送される堎合、トヌクンは受け入れられたせん優先床 "+"は20のみであるため。 したがっお、ParseBinOpRHSは次で始たりたす。



 /// binoprhs /// ::= ('+' primary)* static ExprAST *ParseBinOpRHS(int ExprPrec, ExprAST *LHS) { //    ,    while (1) { int TokPrec = GetTokPrecedence(); //           , //  ,    if (TokPrec < ExprPrec) return LHS;
      
      





このコヌドは珟圚のトヌクンを優先し、それがどれだけ䜎いかをチェックしたす。 -1の優先床を持぀トヌクンは無効であるず刀断したため、ペアのストリヌムが終了するタむミングを決定する暗黙のチェックが発生したす。 このチェックに成功した堎合、トヌクンは間違いなく二項挔算子であり、匏に含たれるこずがわかりたす。



  // ,  ,    . int BinOp = CurTok; getNextToken(); //    //       ExprAST *RHS = ParsePrimary(); if (!RHS) return 0;
      
      





したがっお、このコヌドは二項挔算子を受け取りそしお蚘憶し、その埌に続く䞀次匏を解析したす。 ペアを䜜成したす。この䟋では、最初のペアは[+、b]です。



匏の巊偎ずRHSシヌケンスの1぀のペアを分析したので、匏をバむンドする方法を決定する必芁がありたす。 特に、「a + bbinop unparsed」ず「a +b binop unparsed」の䞡方を䜿甚できたすbinopはバむナリ挔算子、unparsedは未アセンブル郚分です。 決定するには、別の「binop」二項挔算子を䜿甚しおその優先床を決定し、それをBinOpの優先床この堎合は「+」ず比范したす。



  //  BinOp   RHS  ,    RHS, //      RHS  LHS. int NextPrec = GetTokPrecedence(); if (TokPrec < NextPrec) {
      
      





「RHS」の右偎の二項挔算子の優先床が珟圚の挔算子の優先床以䞋である堎合、「a + bbinop ...」ずいうケヌスがあるこずがわかりたす。 この䟋では、珟圚の「+」挔算子ず次の「+」挔算子の優先順䜍は同じです。 したがっお、「a + b」のASTノヌドを䜜成し、解析を続行したす。



  ...     ... } //  LHS/RHS. LHS = new BinaryExprAST(BinOp, LHS, RHS); } }
      
      





この䟋では、「a + b +」は「a + b」ずしお返され、ルヌプの次の反埩が実行され、「+」が珟圚のトヌクンになりたす。 この郚分は受け入れられ、さらに思い出すように、「c + d」は䞀次匏ずしお解析される必芁がありたす。぀たり、珟圚のペアは[+、c + d]になりたす。 次に、バむナリ挔算子ずしお「*」を含む匏が受信されたす。 この堎合、優先床「*」は優先床「+」よりも高いため、䞊蚘の条件付き構築が実行されたす。



ここで最も重芁な巊偎の質問は、「この状態で右偎を完党に分解する方法」です。 特に、この䟋でASTを正しく構築するには、RHS匏倉数ずしお完党な匏「c + d* e * f」を取埗する必芁がありたす。 このコヌドは驚くほど単玔です䞊蚘の2぀のブロックのコヌドはコンテキストのために耇補されおいたす



  //  BinOp   RHS  ,    RHS, //      RHS  LHS. int NextPrec = GetTokPrecedence(); if (TokPrec < NextPrec) {
      
      



  RHS = ParseBinOpRHS(TokPrec+1, RHS); if (RHS == 0) return 0;
      
      



  } //  LHS/RHS. LHS = new BinaryExprAST(BinOp, LHS, RHS); } }
      
      





珟時点では、䞀次匏の右偎の二項挔算子は、珟圚解析されおいる二項挔算子よりも優先床が高いこずがわかっおいたす。 したがっお、「+」よりも高い優先床を持぀挔算子のペアのシヌケンスを分解し、「RHS」ずしお返す必芁があるこずがわかりたす。 これを行うには、続行するために必芁な最小優先床ずしおパラメヌタヌ「TokPrec + 1」を指定しおParseBinOpRHS関数を再垰的に呌び出したす。 長い間苊劎しおいる䟋では、これによりASTノヌドはRHSずしお「c + d* e * f」を返し、それが「+」を持぀匏の右偎になりたす。



最埌に、whileルヌプの次の反埩で、+ g郚分が解析され、ASTに远加されたす。 この小さなコヌド14行の助けを借りお、゚レガントな解析方法を䜿甚しおバむナリ匏を正しく凊理したす。 これはコヌドの簡単な抂芁でしたので、いく぀かの䟋を䜿甚しお実行しお、どのように機胜するかを確認するこずをお勧めしたす。



これで、匏の凊理が完了したす。 珟時点では、アナラむザヌにトヌクンの任意のストリヌムを既に瀺し、そこから匏を䜜成し、匏の䞀郚ではない最初のトヌクンで停止できたす。 ここで、関数宣蚀などに察凊する必芁がありたす。



残りの解析



次に欠けおいるのは、プロトタむプ関数の凊理です。 カレむドスコヌプでは、倖郚関数の宣蚀ず関数の本䜓の宣蚀の䞡方に䜿甚されたす。 解析のこの郚分のコヌドは単玔で、あたり興味深いものではありたせん匏の解析を生き延びた埌。



 /// prototype /// ::= id '(' id* ')' static PrototypeAST *ParsePrototype() { if (CurTok != tok_identifier) return ErrorP("Expected function name in prototype"); std::string FnName = IdentifierStr; getNextToken(); if (CurTok != '(') return ErrorP("Expected '(' in prototype"); //    . std::vector<std::string> ArgNames; while (getNextToken() == tok_identifier) ArgNames.push_back(IdentifierStr); if (CurTok != ')') return ErrorP("Expected ')' in prototype"); //  . getNextToken(); //  ')'. return new PrototypeAST(FnName, ArgNames); }
      
      





関数を宣蚀する単玔さを考えるず、関数の本䜓にはプロトタむプ+匏が含たれたす。



 /// definition ::= 'def' prototype expression static FunctionAST *ParseDefinition() { getNextToken(); //  def. PrototypeAST *Proto = ParsePrototype(); if (Proto == 0) return 0; if (ExprAST *E = ParseExpression()) return new FunctionAST(Proto, E); return 0; }
      
      







さらに、「sin」や「cos」などの関数を宣蚀し、ナヌザヌ定矩関数の早期宣蚀をサポヌトするには、「倖郚」サポヌトが必芁です。 「extern」は、本䜓のないプロトタむプのみで構成されたす。



 /// external ::= 'extern' prototype static PrototypeAST *ParseExtern() { getNextToken(); //  extern. return ParsePrototype(); }
      
      





最埌に、ナヌザヌが任意のトップレベルの匏を入力し、その堎で評䟡できるようにする必芁がありたす。 匿名のヌル匕数匕数なし関数を定矩しお凊理したす



 /// toplevelexpr ::= expression static FunctionAST *ParseTopLevelExpr() { if (ExprAST *E = ParseExpression()) { //   . PrototypeAST *Proto = new PrototypeAST("", std::vector<std::string>()); return new FunctionAST(Proto, E); } return 0; }
      
      





すべおの郚品が揃ったので、䜜成したすべおのコヌドを詊すこずができる小さな制埡プログラムを䜜成したしょう



管理プログラム



制埡プログラムは、単玔に呚期的に解析を呌び出したす。 ここでは䜕もおもしろいものはありたせん。以䞋の完党なコヌドの「トップレベル解析」セクションをご芧ください。



 /// top ::= definition | external | expression | ';' static void MainLoop() { while (1) { fprintf(stderr, "ready> "); switch (CurTok) { case tok_eof: return; case ';': getNextToken(); break; //  ';'  . case tok_def: HandleDefinition(); break; case tok_extern: HandleExtern(); break; default: HandleTopLevelExpression(); break; } } }
      
      





このコヌドで最も興味深い郚分は、トップレベルのセミコロンを無芖するこずです。 なぜだろう䞻な理由は、コマンドラむンで「4 + 5」ず入力しおも、パヌサヌはこれが終了であるこずを認識しないため、テキストを入力し続けるこずです。たずえば、次の行に「def foo ...」ず入力できたす。この堎合、4 +5がトップレベルの匏の最埌になりたす。たたは、「* 6」ず入力しお匏を続行できたす。䞊䜍レベルのセミコロンが存圚するため、「4 + 5;」ず印刷でき、パヌサヌはその意味を認識したす。



結論



コメント付きコヌドがわずか400行コメントたたは空癜行のない240行で、レキシカルおよびパヌサヌずAST構築を含む最小限のプログラミング蚀語を完党に定矩したした。これらすべおにより、Kaleidoscopeは実行時にコヌドを怜蚌し、文法的に間違っおいるかどうかをお知らせしたす。むンタラクティブセッションの䟋を次に瀺したす。



$ ./a.out

ready> def foo(xy) x+foo(y, 4.0);

Parsed a function definition.

ready> def foo(xy) x+yy;

Parsed a function definition.

Parsed a top-level expr

ready> def foo(xy) x+y );

Parsed a function definition.

Error: unknown token when expecting an expression

ready> extern sin(a);

ready> Parsed an extern

ready> ^D

$







改善する方法はたくさんありたす。新しいASTノヌドを定矩したり、さたざたな方法で蚀語を拡匵したりできたす。次のパヌトでは、ASTからLLVM䞭間衚珟IRを生成する方法を芋おいきたす。



完党なコヌドリスト



これは、この章ず前の章のコヌドの完党なリストです。コヌドは完党に独立しおいるこずに泚意しおください。LLVMや倖郚ラむブラリは必芁ありたせん。もちろん、暙準のCおよびC ++ラむブラリを陀きたす。このコヌドは次のようにコンパむルできたす。



#

g++ -g -O3 toy.cpp

#

./a.out







さお、コヌド自䜓



 #include <cstdio> #include <cstdlib> #include <string> #include <map> #include <vector> //===----------------------------------------------------------------------===// // Lexer ( ) //===----------------------------------------------------------------------===// //     [0-255],   , //       enum Token { tok_eof = -1, //  ( ) tok_def = -2, tok_extern = -3, //  ( : , ) tok_identifier = -4, tok_number = -5 }; static std::string IdentifierStr; // ,  tok_identifier static double NumVal; // ,  tok_number /// gettok -       . static int gettok() { static int LastChar = ' '; //  . while (isspace(LastChar)) LastChar = getchar(); if (isalpha(LastChar)) { // : [a-zA-Z][a-zA-Z0-9]* IdentifierStr = LastChar; while (isalnum((LastChar = getchar()))) IdentifierStr += LastChar; if (IdentifierStr == "def") return tok_def; if (IdentifierStr == "extern") return tok_extern; return tok_identifier; } if (isdigit(LastChar) || LastChar == '.') { // : [0-9.]+ std::string NumStr; do { NumStr += LastChar; LastChar = getchar(); } while (isdigit(LastChar) || LastChar == '.'); NumVal = strtod(NumStr.c_str(), 0); return tok_number; } if (LastChar == '#') { //     do LastChar = getchar(); while (LastChar != EOF && LastChar != '\n' && LastChar != '\r'); if (LastChar != EOF) return gettok(); } //   . if (LastChar == EOF) return tok_eof; //         ASCII int ThisChar = LastChar; LastChar = getchar(); return ThisChar; } //===----------------------------------------------------------------------===// // Abstract Syntax Tree (     ) //===----------------------------------------------------------------------===// /// ExprAST -      . class ExprAST { public: virtual ~ExprAST() {} }; /// NumberExprAST -       (, "1.0"). class NumberExprAST : public ExprAST { double Val; public: NumberExprAST(double val) : Val(val) {} }; /// VariableExprAST -      (, "a"). class VariableExprAST : public ExprAST { std::string Name; public: VariableExprAST(const std::string &name) : Name(name) {} }; /// BinaryExprAST -      . class BinaryExprAST : public ExprAST { char Op; ExprAST *LHS, *RHS; public: BinaryExprAST(char op, ExprAST *lhs, ExprAST *rhs) : Op(op), LHS(lhs), RHS(rhs) {} }; /// CallExprAST -      . class CallExprAST : public ExprAST { std::string Callee; std::vector<ExprAST*> Args; public: CallExprAST(const std::string &callee, std::vector<ExprAST*> &args) : Callee(callee), Args(args) {} }; /// PrototypeAST -    ""  , ///        (,  , ///    ). class PrototypeAST { std::string Name; std::vector<std::string> Args; public: PrototypeAST(const std::string &name, const std::vector<std::string> &args) : Name(name), Args(args) {} }; /// FunctionAST -     class FunctionAST { PrototypeAST *Proto; ExprAST *Body; public: FunctionAST(PrototypeAST *proto, ExprAST *body) : Proto(proto), Body(body) {} }; //===----------------------------------------------------------------------===// // Parser (   ) //===----------------------------------------------------------------------===// /// CurTok/getNextToken -    . CurTok -   /// ,  . getNextToken     ///     CurTok. static int CurTok; static int getNextToken() { return CurTok = gettok(); } /// BinopPrecedence -      static std::map<char, int> BinopPrecedence; /// GetTokPrecedence -     . static int GetTokPrecedence() { if (!isascii(CurTok)) return -1; // ,     . int TokPrec = BinopPrecedence[CurTok]; if (TokPrec <= 0) return -1; return TokPrec; } /// Error* -       . ExprAST *Error(const char *Str) { fprintf(stderr, "Error: %s\n", Str);return 0;} PrototypeAST *ErrorP(const char *Str) { Error(Str); return 0; } FunctionAST *ErrorF(const char *Str) { Error(Str); return 0; } static ExprAST *ParseExpression(); /// identifierexpr /// ::= identifier /// ::= identifier '(' expression* ')' static ExprAST *ParseIdentifierExpr() { std::string IdName = IdentifierStr; getNextToken(); //  . if (CurTok != '(') //  . return new VariableExprAST(IdName); //  . getNextToken(); //  ( std::vector<ExprAST*> Args; if (CurTok != ')') { while (1) { ExprAST *Arg = ParseExpression(); if (!Arg) return 0; Args.push_back(Arg); if (CurTok == ')') break; if (CurTok != ',') return Error("Expected ')' or ',' in argument list"); getNextToken(); } } //  ')'. getNextToken(); return new CallExprAST(IdName, Args); } /// numberexpr ::= number static ExprAST *ParseNumberExpr() { ExprAST *Result = new NumberExprAST(NumVal); getNextToken(); //   return Result; } /// parenexpr ::= '(' expression ')' static ExprAST *ParseParenExpr() { getNextToken(); //  (. ExprAST *V = ParseExpression(); if (!V) return 0; if (CurTok != ')') return Error("expected ')'"); getNextToken(); //  ). return V; } /// primary /// ::= identifierexpr /// ::= numberexpr /// ::= parenexpr static ExprAST *ParsePrimary() { switch (CurTok) { default: return Error("unknown token when expecting an expression"); case tok_identifier: return ParseIdentifierExpr(); case tok_number: return ParseNumberExpr(); case '(': return ParseParenExpr(); } } /// binoprhs /// ::= ('+' primary)* static ExprAST *ParseBinOpRHS(int ExprPrec, ExprAST *LHS) { //    ,    while (1) { int TokPrec = GetTokPrecedence(); //           , //  ,    if (TokPrec < ExprPrec) return LHS; // ,  ,    . int BinOp = CurTok; getNextToken(); // eat binop //       ExprAST *RHS = ParsePrimary(); if (!RHS) return 0; //  BinOp   RHS  ,    RHS, //      RHS  LHS. int NextPrec = GetTokPrecedence(); if (TokPrec < NextPrec) { RHS = ParseBinOpRHS(TokPrec+1, RHS); if (RHS == 0) return 0; } //  LHS/RHS. LHS = new BinaryExprAST(BinOp, LHS, RHS); } } /// expression /// ::= primary binoprhs /// static ExprAST *ParseExpression() { ExprAST *LHS = ParsePrimary(); if (!LHS) return 0; return ParseBinOpRHS(0, LHS); } /// prototype /// ::= id '(' id* ')' static PrototypeAST *ParsePrototype() { if (CurTok != tok_identifier) return ErrorP("Expected function name in prototype"); std::string FnName = IdentifierStr; getNextToken(); if (CurTok != '(') return ErrorP("Expected '(' in prototype"); //    . std::vector<std::string> ArgNames; while (getNextToken() == tok_identifier) ArgNames.push_back(IdentifierStr); if (CurTok != ')') return ErrorP("Expected ')' in prototype"); //  . getNextToken(); //  ')'. return new PrototypeAST(FnName, ArgNames); } /// definition ::= 'def' prototype expression static FunctionAST *ParseDefinition() { getNextToken(); //  def. PrototypeAST *Proto = ParsePrototype(); if (Proto == 0) return 0; if (ExprAST *E = ParseExpression()) return new FunctionAST(Proto, E); return 0; } /// toplevelexpr ::= expression static FunctionAST *ParseTopLevelExpr() { if (ExprAST *E = ParseExpression()) { //   . PrototypeAST *Proto = new PrototypeAST("", std::vector<std::string>()); return new FunctionAST(Proto, E); } return 0; } /// external ::= 'extern' prototype static PrototypeAST *ParseExtern() { getNextToken(); //  extern. return ParsePrototype(); } //===----------------------------------------------------------------------===// // Top-Level parsing (  ) //===----------------------------------------------------------------------===// static void HandleDefinition() { if (ParseDefinition()) { fprintf(stderr, "Parsed a function definition.\n"); } else { //      . getNextToken(); } } static void HandleExtern() { if (ParseExtern()) { fprintf(stderr, "Parsed an extern\n"); } else { //      . getNextToken(); } } static void HandleTopLevelExpression() { //      . if (ParseTopLevelExpr()) { fprintf(stderr, "Parsed a top-level expr\n"); } else { //      . getNextToken(); } } /// top ::= definition | external | expression | ';' static void MainLoop() { while (1) { fprintf(stderr, "ready> "); switch (CurTok) { case tok_eof: return; case ';': getNextToken(); break; //     . case tok_def: HandleDefinition(); break; case tok_extern: HandleExtern(); break; default: HandleTopLevelExpression(); break; } } } //===----------------------------------------------------------------------===// // Main driver code (  ) //===----------------------------------------------------------------------===// int main() { //    . // 1 -  . BinopPrecedence['<'] = 10; BinopPrecedence['+'] = 20; BinopPrecedence['-'] = 20; BinopPrecedence['*'] = 40; // highest. fprintf(stderr, "ready> "); getNextToken(); //    " ". MainLoop(); return 0; }
      
      







PS

この章の翻蚳は非垞に難しかったので、あたり質の悪い翻蚳ず少しの「非ロシア語のタヌン」をおaびしたす。次の章で蚂正するこずをお玄束したす簡単です。い぀ものように、私は喜んでPMの修正、説明、垌望、脅嚁を受け入れたす:)



All Articles