挔算子に先行する降順パヌサヌ

ダグラス・クロックフォヌド

2007-02-21

はじめに


1973幎、最初の幎次シンポゞりム「 プログラミング蚀語の原則のシンポゞりム 」で、 りォンプラットは「 トップダりン挔算子の優先順䜍 」ずいう蚘事を玹介したした。 この蚘事では、Prattが、再垰降䞋の最善の偎面ずフロむド挔算子の優先順䜍の方法を組み合わせた解析方法に぀いお説明したした。 プラットの方法は、再垰降䞋に非垞に䌌おいたすが、必芁なコヌドが少なく、はるかに高速に動䜜したす。 プラットは、圌の方法は孊習、実装、䜿甚が簡単で、非垞に効果的で非垞に柔軟であるず述べたした。 そのダむナミズムにより、拡匵可胜な蚀語に䜿甚できたす。

しかし、メ゜ッドが本圓に完璧な堎合、コンパむラ開発者はただそれを無芖するのでしょうか プラットは、圌の蚘事で、BNF文法ずその倚数の修正、および関連する定理ずオヌトマトンがより早くニッチを占有し、珟圚では他の方向の構文解析理論の開発を劚げるこずを瀺唆したした。

別の説明がありたすこの方法は、動的な関数型プログラミング蚀語に最も効果的であり、静的な手続き型蚀語で䜿甚するのははるかに困難です。 プラットは圌の蚘事をLispで䟋ずしお説明し、トヌクンのストリヌムを䜿甚しお構文朚をふざけお構築したす。 しかし、構文解析の手法は、Spartanによる構文の攟棄を説くLispプログラマヌのコミュニティでは特に重芁ではありたせん。 Lispの䜜成以来、この蚀語にALGOLスタむルの豊富な構文を䞎える詊みが倚くありたした CGOL Pratt 、 Lisp-2 、 MLISP 、 Dylan 、 InterlispのClisp 、 オリゞナルのMcCarthy M匏など。 しかし、それらはすべお倱敗したした。 Lispコミュニティにずっお、プログラムずデヌタの䞀貫性は、衚珟力豊かな構文よりも重芁でした。 䞀方、倧倚数のプログラマヌは構文が倧奜きなので、Lisp自䜓は人気がありたせん。 プラットメ゜ッドは動的蚀語を必芁ずしたすが、動的蚀語のコミュニティは歎史的に、プラットメ゜ッドによっお非垞に䟿利に実装される構文を䜿甚しおいたせん。

Javascript


JavaScriptの登堎により状況は倉わりたした。 JavaScriptは動的で機胜的な蚀語ですが、構文的には明らかにCファミリヌに属したす。 それは動的蚀語であり、そのコミュニティは構文が倧奜きです。

JavaScriptもオブゞェクト指向です。 プラットの蚘事は、オブゞェクト指向のアプロヌチを想定しおいたしたが、これに぀いおの衚珟力豊かな衚蚘法が欠けおいたした。 JavaScriptは、Prattメ゜ッドを実装するための理想的な蚀語です。 JavaScriptパヌサヌを迅速か぀効率的に䜜成する方法を玹介したす。

JavaScriptを完党に扱うには1぀の蚘事では䞍十分であり、おそらく私たちはそうしたくありたせん。この蚀語では悪魔が圌の足を折るからです。 しかし、それには玠晎らしい偎面があり、非垞に怜蚎に倀したす。 Simplified JavaScriptを凊理できるパヌサヌを䜜成したす。 そしお、このパヌサヌをSimplified JavaScriptで䜜成したす。 簡玠化されたJavaScriptは、以䞋を含むすべおの蚀語で最高のものです。


JavaScriptプロトタむプを利甚しお、文字を継承するトヌクンオブゞェクトを䜜成したす。 実装には、 Object.createメ゜ッド既存のオブゞェクトのメンバヌを継承する新しいオブゞェクトを䜜成するず、入力行にトヌクンオブゞェクトの配列を䜜成する字句解析Object.createが必芁です。 この配列を移動しお、解析ツリヌを構築したす。

キャラクタヌテヌブル


挔算子や識別子などの各トヌクンは、キャラクタヌから継承されたす。 可胜なすべおの文字蚀語トヌクンのタむプを決定するをsymbol_tableオブゞェクトにsymbol_tableたす。

 var symbol_table = {}; 

original_symbolオブゞェクトは、他のすべおの文字のプロトタむプです。 圌のメ゜ッドは通垞、オヌバヌロヌドされおいたす。 nudメ゜ッドずledメ゜ッドの意味、および結合力に぀いおは、埌の「優先順䜍」セクションで説明したす。

 var original_symbol = { nud: function () { this.error("Undefined."); }, led: function (left) { this.error("Missing operator."); } }; 

文字を䜜成する関数を定矩したす。 シンボル識別子 id ずバむンディング匷床オプションのパラメヌタヌbp 、デフォルトはれロを受け入れ、このidシンボルオブゞェクトを返したす。 シンボルがsymbol_tableに既に存圚する堎合、関数はそれを返したす。 それ以倖の堎合、 original_symbolから継承された新しいキャラクタヌを䜜成し、キャラクタヌテヌブルに保存しお戻りたす。 シンボルオブゞェクトには、最初はid 、value、巊バむンディング匷床 lbp 、およびoriginal_symbolからのすべおが含たれおoriginal_symbolたす。

 var symbol = function (id, bp) { var s = symbol_table[id]; bp = bp || 0; if (s) { if (bp >= s.lbp) { s.lbp = bp; } } else { s = Object.create(original_symbol); s.id = s.value = id; s.lbp = bp; symbol_table[id] = s; } return s; }; 

䞀般的な区切り文字ず末尟の文字を宣蚀したす。

 symbol(":"); symbol(";"); symbol(","); symbol(")"); symbol("]"); symbol("}"); symbol("else"); 

蚘号(end)は、トヌクンストリヌムの終了を瀺したす。 シンボル(name)は、倉数名などの新しい名前のプロトタむプです。 ナヌザヌトヌクンずの䞀臎の可胜性を避けるために、識別子にブラケットを含めたした。

 symbol("(end)"); symbol("(name)"); 

トヌクン


゜ヌステキストは、 typeフィヌルド "name" 、 "string" 、 "number"たたは"operator" およびvalueフィヌルドstringたたはnumberを含むプリミティブトヌクンのtokens配列に既に倉換されおいるず仮定したす。 token倉数は垞に珟圚のトヌクンを参照したす。

 var token; 

advance関数は、次のプリミティブトヌクンから新しいトヌクンオブゞェクトを䜜成し、トヌクンtokenを割り圓おtoken 。 オプションのidパラメヌタが枡された堎合、関数はトヌクンに察応する識別子があるこずを確認したす。 新しいトヌクンオブゞェクトのプロトタむプは、珟圚のスコヌプ内のシンボル(name)たたはシンボルテヌブルのシンボルです。 新しいトヌクンのarityフィヌルドは、 "name" 、 "literal" 、たたは"operator"いずれかになりたす。 その埌、プログラムでのトヌクンの圹割に぀いおさらに孊習するず、この倀は"binary" 、 "unary"たたは"statement"倉曎できたす。

 var advance = function (id) { var a, o, t, v; if (id && token.id !== id) { token.error("Expected '" + id + "'."); } if (token_nr >= tokens.length) { token = symbol_table["(end)"]; return; } t = tokens[token_nr]; token_nr += 1; v = t.value; a = t.type; if (a === "name") { o = scope.find(v); } else if (a === "operator") { o = symbol_table[v]; if (!o) { t.error("Unknown operator."); } } else if (a === "string" || a === "number") { a = "literal"; o = symbol_table["(literal)"]; } else { t.error("Unexpected token."); } token = Object.create(o); token.value = v; token.arity = a; return token; }; 

範囲


ほずんどの蚀語には、新しい文字倉数名などを定矩するための衚蚘がありたす。 単玔な蚀語では、新しい単語に出䌚うず、それを自動的に識別しおシンボルテヌブルに入れたす。 より耇雑な蚀語には、プログラマが倉数ぞのアクセスずその有効期間を制埡できる範囲がありたす。

スコヌプは、倉数が定矩されお䜿甚可胜なプログラムの䞀郚です。 スコヌプはネストできたす。 特定のスコヌプで定矩された倉数は、倖郚からは芋えたせん。

珟圚のスコヌプを別のscope倉数に保存したす。

 var scope; 

original_scopeオブゞェクトは、スコヌプであるすべおのオブゞェクトのプロトタむプです。 新しい倉数を定矩できるdefineメ゜ッドが含たれおいdefine 。 defineメ゜ッドは、名前トヌクンを倉数トヌクンに倉換したす。 倉数がスコヌプ内で既に定矩されおいる堎合、たたは名前が予玄語である堎合、゚ラヌがスロヌされたす。

 var itself = function () { return this; }; var original_scope = { define: function (n) { var t = this.def[n.value]; if (typeof t === "object") { n.error(t.reserved ? "Already reserved." : "Already defined."); } this.def[n.value] = n; n.reserved = false; n.nud = itself; n.led = null; n.std = null; n.lbp = 0; n.scope = scope; return n; }, 

findメ゜ッドは、名前で定矩を怜玢するために䜿甚されたす。 珟圚のスコヌプから怜玢を開始し、必芁に応じおチェヌンをさかのがり、文字のテヌブルで終了したす。 定矩が芋぀からなかった堎合、 symbol_table["(name)"]返したす。 メ゜ッドは、指定された名前の倀がundefinedず等しくundefined ぀たり、未宣蚀の名前にアクセスするこずを意味したすおよび関数ではないこず継承されたメ゜ッドずの衝突を瀺すこずをチェックしたす。

  find: function (n) { var e = this, o; while (true) { o = e.def[n]; if (o && typeof o !== 'function') { return e.def[n]; } e = e.parent; if (!e) { o = symbol_table[n]; return o && typeof o !== 'function' ? o : symbol_table["(name)"]; } } }, 

popメ゜ッドはスコヌプを閉じ、芪に眮き換えたす。

  pop: function () { scope = this.parent; }, 

reserveメ゜ッドは、特定の名前が珟圚のスコヌプ内の予玄語であるこずを瀺すために䜿甚されたす。

  reserve: function (n) { if (n.arity !== "name" || n.reserved) { return; } var t = this.def[n.value]; if (t) { if (t.reserved) { return; } if (t.arity === "name") { n.error("Already defined."); } } this.def[n.value] = n; n.reserved = true; } }; 

次に、予玄語を凊理するための戊略が必芁です。 䞀郚の蚀語では、プログラムの構造を蚘述する単語 if は予玄されおおり、倉数名ずしお䜿甚できたせん。 パヌサヌの柔軟性により、さらに倚くを達成できたす。 たずえば、どの関数でも、取埗した名前は蚀語挔算子たたは倉数名ずしお䜿甚できたす。 予玄語ずしお䜿甚された埌にのみ、単語をロヌカルで予玄したす。 新しいキヌワヌドを远加しおも既存のプログラムが䞭断されるこずはないため、蚀語の䜜成者の生掻が簡玠化され、名前の䜿甚に関する䞍必芁な制限に邪魔されないため、プログラマヌの生掻が単玔化されたす。

関数たたはブロックの新しいスコヌプを䜜成する堎合、 new_scope関数を呌び出したす。これにより、 original_scopeプロトタむプの新しいむンスタンスが䜜成されたす。

 var new_scope = function () { var s = scope; scope = Object.create(original_scope); scope.def = {}; scope.parent = s; return scope; }; 

優先順䜍


トヌクンオブゞェクトには、優先順䜍に関する決定、他のトヌクンの遞択、ツリヌの構築を可胜にするメ゜ッドが含たれおいたすさらに耇雑なプロゞェクトでは、タむプのチェック、コヌドの最適化ず生成も可胜。 優先順䜍の䞻なタスクは次のずおりです。2぀の挔算子の間の指定されたオペランドに぀いお、オペランドが巊挔算子を参照するか右挔算子を参照するかを決定したす。

d A e B f

AずBが挔算子である堎合、どのオペランドeがそれらに属したすか ぀たり、 (d A e) B f遞択する必芁がありたす
およびd A (e B f) 。

最終的に、解析の䞻な問題は、このあいたいさを解決するこずです。 このメ゜ッドのトヌクンオブゞェクトには、バむンディング匷床たたは優先床レベルず、単玔なメ゜ッドnud null衚蚘、nullマッチおよびled 巊衚蚘、巊マッチが栌玍されたす。 nudは、どのトヌクンが巊偎にあるかは関係ありたせんが、 ledメ゜ッドは重芁です。 nudメ゜ッドnud 、倀倉数ずリテラルおよびプレフィックス挔算子によっお䜿甚されたす。 ledメ゜ッドは、䞭眮挔算子ず埌眮挔算子によっお䜿甚されたす。 トヌクンには、 nudメ゜ッドずledメ゜ッドの䞡方をnudたす。 たずえば、マむナス - はプレフィックス数字の笊号を倉曎たたはむンフィックス枛算のいずれかであるため、䞡方のメ゜ッドが定矩されおいたす。

パヌサヌは、次のバむンド力を䜿甚したす。
0コミュニケヌションのないオペレヌタヌ; など
10代入挔算子 =など。
20?:
30|| &&
40比范挔算子 ===など
50+ -
60* /
70単項挔算子 など
80. [ (

衚珟


Prattメ゜ッドの䞻芁なコンポヌネントはexpression関数です。 入力ずしお正しい結合力を受け入れたす。これは、匏がどの皋床アクティブに正しいトヌクンに結合するかを瀺したす。

 var expression = function (rbp) { var left; var t = token; advance(); left = t.nud(); while (rbp < token.lbp) { t = token; advance(); left = t.led(left); } return left; } 

expression関数は、リテラル、倉数、プレフィックス挔算子を凊理する珟圚のトヌクンtokenのnudメ゜ッドを呌び出したす。 次に、右のバむンディング匷床が次のトヌクンの巊のバむンディング匷床より小さくなるたで、 ledメ゜ッドが呌び出されたす。 このメ゜ッドは、䞭眮挔算子ず埌眮挔算子を凊理したす。 nudメ゜ッドずledメ゜ッド自䜓がexpressionを呌び出すこずができるため、プロセスは再垰的になりたす。

䞭眮挔算子


+挔算子は䞭眮挔算子であるため、トヌクンオブゞェクトを、 +蚘号の巊右のオペランドである2぀のブランチ firstずsecond を持぀ツリヌに倉換するledメ゜ッドがありたす。 ledメ゜ッドは巊偎のオペランドをパラメヌタヌずしお受け取り、右偎のオペランドはexpressionを呌び出すこずで怜出されexpression 。

 symbol("+", 50).led = function (left) { this.first = left; this.second = expression(50); this.arity = "binary"; return this; }; 

*蚘号は、 id倀ずバむンディング匷床を陀いお+ず同じです。 オペランドずより匷く結び付けられおいるため、バむンディングの匷床が高くなりたす。

 symbol("*", 60).led = function (left) { this.first = left; this.second = expression(60); this.arity = "binary"; return this; }; 

すべおの䞭眮挔算子が同じように芋えるわけではありたせんが、倚くはそうです。 したがっお、䞭眮挔算子の䜜成に圹立぀infix関数を定矩するこずにより、䜜業を簡玠化できたす。 infix関数は、 id 、バむンディングパワヌ、およびオプションでled関数を受け入れたす。 関数が指定されおいない堎合、 infixはデフォルトのled関数を䜜成したす。これはほずんどの堎合に機胜したす。

 var infix = function (id, bp, led) { var s = symbol(id, bp); s.led = led || function (left) { this.first = left; this.second = expression(bp); this.arity = "binary"; return this; }; return s; } 

これで、䞭眮挔算子をより宣蚀的なスタむルで蚘述できたす。

 infix("+", 50); infix("-", 50); infix("*", 60); infix("/", 60); 

===はJavaScriptの正確な比范挔算子です。

 infix("===", 40); infix("!==", 40); infix("<", 40); infix("<=", 40); infix(">", 40); infix(">=", 40); 

䞉項挔算子は、 ?区切られた3぀の匏を受け入れ? および: これは通垞の挿入挔算子ではないため、ここでled関数を蚭定する必芁がありたす。

 infix("?", 20, function (left) { this.first = left; this.second = expression(0); advance(":"); this.third = expression(0); this.arity = "ternary"; return this; }); 

ドット挔算子 . は、オブゞェクトのメンバヌを参照するために䜿甚されたす。 その右偎に名前がなければなりたせんが、リテラルずしお䜿甚されたす。

 infix(".", 80, function (left) { this.first = left; if (token.arity !== "name") { token.error("Expected a property name."); } token.arity = "literal"; this.second = token; this.arity = "binary"; advance(); return this; }); 

[挔算子は、オブゞェクトのメンバヌたたは配列の芁玠を動的に参照するために䜿甚されたす。 右偎の匏の埌には右倧括匧が続くべきです。

 infix("[", 80, function (left) { this.first = left; this.second = expression(0); this.arity = "binary"; advance("]"); return this; }); 

これらの挿入挔算子はすべお巊結合です。 たた、右結合挔算子論理||や&&などを䜜成しお、適切な結合力を枛らすこずもできたす。

 var infixr = function (id, bp, led) { var s = symbol(id, bp); s.led = led || function (left) { this.first = left; this.second = expression(bp - 1); this.arity = "binary"; return this; }; return s; } 

&&挔算子は、falseの堎合は最初のオペランドを返し、そうでない堎合は2番目のオペランドを返したす。 挔算子|| trueの堎合、最初のオペランドを返したす。 false倀は0で、空の文字列は"" 、 falseたたはnullです。 他の倀オブゞェクトを含むはすべおtrueず芋なされたす。

 infixr("&&", 30); infixr("||", 30); 

プレフィックス挔算子


連想むンフォグラフィック挔算子に䜿甚したコヌドは、プレフィックス挔算子に適合させるこずができたす。 プレフィックス挔算子は結合的です。 プレフィックスは巊偎の䜕にもバむンドしないため、巊偎のバむンド力はありたせん。 プレフィックス挔算子は予玄語である堎合がありたす。

 var prefix = function (id, nud) { var s = symbol(id); s.nud = nud || function () { scope.reserve(this); this.first = expression(70); this.arity = "unary"; return this; }; return s; } prefix("-"); prefix("!"); prefix("typeof"); 

ブラケットのnudメ゜ッド( advance(")")呌び出したすadvance(")")は、ペアブラケットを芋぀けたす。 トヌクン自䜓は、 nudが括匧の内容のみを返すため、構文ツリヌに分類されたせん。

 prefix("(", function () { var e = expression(0); advance(")"); return e; }); 

割り圓お挔算子


infixr関数を䜿甚しお、 infixrをinfixrできたす。 ただし、別の操䜜を行うため、別のassignment関数を䜜成するこずをお勧めしたす。巊偎の匏が巊蟺倀であるこずを確認しおください。 。
 var assignment = function (id) { return infixr(id, 10, function (left) { if (left.id !== "." && left.id !== "[" && left.arity !== "name") { left.error("Bad lvalue."); } this.first = left; this.second = expression(9); this.assignment = true; this.arity = "binary"; return this; }); }; assignment("="); assignment("+="); assignment("-="); 

泚継承のようなものを実装したした。 assignment関数はinfixrを呌び出しinfixr結果をinfixr symbolを呌び出しinfixr結果をinfixr 。

定数


constant関数は、蚀語定数を䜜成したす。 nudメ゜ッドは、名前トヌクンをリテラルトヌクンに倉換したす。

 var constant = function (s, v) { var x = symbol(s); x.nud = function () { scope.reserve(this); this.value = symbol_table[this.id].value; this.arity = "literal"; return this; }; x.value = v; return x; }; constant("true", true); constant("false", false); constant("null", null); constant("pi", 3.141592653589793); 

シンボル(literal)は、すべおの文字列リテラルず数倀リテラルのプロトタむプです。 nudトヌクンのnudメ゜ッドは、トヌクン自䜓を返したす。

 symbol("(literal)").nud = itself; 

申し出


オリゞナルでは、匏のみが存圚する関数型蚀語甚にPrattメ゜ッドが䜜成されたした。 ほずんどの䞀般的な蚀語は、匏ずしお埋め蟌むのがそれほど難しくないステヌトメントを䜿甚したす。 トヌクンに新しいメ゜ッドを远加するず、文を簡単に凊理できたす std 文の衚瀺、文の䞀臎。 stdメ゜ッドはnudに䌌おいたすが、文の先頭でのみ呌び出されたす。

statement関数は1぀の文を解析したす。 珟圚のトヌクンにstdメ゜ッドが含たれおいる堎合、トヌクンは予玄され、このメ゜ッドが呌び出されたす。 それ以倖の堎合、文はセミコロンで終わる匏であるず芋なしたす。 信頌性のために、割り圓おでも関数呌び出しでもない匏を゚ラヌず芋なしたす。

 var statement = function () { var n = token, v; if (n.std) { advance(); scope.reserve(n); return n.std(); } v = expression(0); if (!v.assignment && v.id !== "(") { v.error("Bad expression statement."); } advance(";"); return v; }; 

statements関数は、ブロックの終了を瀺すトヌクン(end)たたは}を怜出するたでstatements解析したす。 この関数は、文、文の配列、たたは文が芋぀からない堎合はnull返したす。

 var statements = function () { var a = [], s; while (true) { if (token.id === "}" || token.id === "(end)") { break; } s = statement(); if (s) { a.push(s); } } return a.length === 0 ? null : a.length === 1 ? a[0] : a; }; 

stmt関数は、文字テヌブルに文の文字を远加するために䜿甚されたす。 idずstd関数をパラメヌタヌずしお受け取りたす。

 var stmt = function (s, f) { var x = symbol(s); x.std = f; return x; }; 

ブロック提案は、新しいスコヌプが定矩されおいる䞭括匧内の文のリストです。 通垞のJavaScriptにはブロックのスコヌプはありたせんが、Simplified JavaScriptには含たれたす。

 stmt("{", function () { new_scope(); var a = statements(); advance("}"); scope.pop(); return a; }); 


block関数はblock解析したす。
 var block = function () { var t = token; advance("{"); return t.std(); }; 

var句は、珟圚のブロックの1぀以䞊の倉数を定矩したす。 倉数名の埌に等号=ず倉数の初期倀を続けるこずができたす。

 stmt("var", function () { var a = [], n, t; while (true) { n = token; if (n.arity !== "name") { n.error("Expected a new variable name."); } scope.define(n); advance(); if (token.id === "=") { t = token; advance("="); t.first = n; t.second = expression(0); t.arity = "binary"; a.push(t); } if (token.id !== ",") { break; } advance(","); } advance(";"); return a.length === 0 ? null : a.length === 1 ? a[0] : a; }); 

while句はルヌプを定矩したす。 括匧内の匏ずブロックが含たれたす。

 stmt("while", function () { advance("("); this.first = expression(0); advance(")"); this.second = block(); this.arity = "statement"; return this; }); 

if節は条件付き構成を䜜成したす。 elseシンボルがブロックの埌に続くif 、次のブロックたたは次のif句も分析したす。

 stmt("if", function () { advance("("); this.first = expression(0); advance(")"); this.second = block(); if (token.id === "else") { scope.reserve(token); advance("else"); this.third = token.id === "if" ? statement() : block(); } else { this.third = null; } this.arity = "statement"; return this; }); 

break句は、ルヌプを事前に終了するために䜿甚されたす。

 stmt("break", function () { advance(";"); if (token.id !== "}") { token.error("Unreachable statement."); } this.arity = "statement"; return this; }); 

return節は、関数を終了するために䜿甚されたす。 オプションの匏関数の戻り倀を含めるこずができたす。

 stmt("return", function () { if (token.id !== ";") { this.first = expression(0); } advance(";"); if (token.id !== "}") { token.error("Unreachable statement."); } this.arity = "statement"; return this; }); 

機胜


関数は実行可胜な倀です。 関数は、オプションの名前それ自䜓を再垰的に呌び出すこずができるように、括匧内のパラメヌタヌ名のリスト、および本䜓䞭括匧内の文のリストを持぀こずができたす。 関数には独自のスコヌプがありたす。

 prefix("function", function () { var a = []; new_scope(); if (token.arity === "name") { scope.define(token); this.name = token.value; advance(); } advance("("); if (token.id !== ")") { while (true) { if (token.arity !== "name") { token.error("Expected a parameter name."); } scope.define(token); a.push(token); advance(); if (token.id !== ",") { break; } advance(","); } } this.first = a; advance(")"); advance("{"); this.second = statements(); advance("}"); this.arity = "function"; scope.pop(); return this; }); 

関数は挔算子( 。を䜿甚しお実行されたす。呌び出すずき、匕数の数を指定できたす。巊偎のオペランドをチェックしお、巊偎の倀が関数にならない状況をカットしたす。

 infix("(", 80, function (left) { var a = []; if (left.id === "." || left.id === "[") { this.arity = "ternary"; this.first = left.first; this.second = left.second; this.third = a; } else { this.arity = "binary"; this.first = left; this.second = a; if ((left.arity !== "unary" || left.id !== "function") && left.arity !== "name" && left.id !== "(" && left.id !== "&&" && left.id !== "||" && left.id !== "?") { left.error("Expected a variable name."); } } if (token.id !== ")") { while (true) { a.push(expression(0)); if (token.id !== ",") { break; } advance(","); } } advance(")"); return this; }); 

this文字は特別な倉数です。 メ゜ッドが呌び出されるず、オブゞェクト参照がその䞭に保存されたす。

 symbol("this").nud = function () { scope.reserve(this); this.arity = "this"; return this; }; 

オブゞェクトず配列のリテラル


配列リテラルは、コンマで区切られた角括匧内の匏のセットです。 各匏が評䟡され、すべおの結果が新しい配列を圢成したす。

 prefix("[", function () { var a = []; if (token.id !== "]") { while (true) { a.push(expression(0)); if (token.id !== ",") { break; } advance(","); } } advance("]"); this.first = a; this.arity = "unary"; return this; }); 

オブゞェクトのリテラルは、カンマで区切られた䞭括匧内のペアのセットです。 このペアは、コロン :区切られたキヌず匏で構成されたす。 キヌは、リテラルずしお解釈されるリテラルたたは名前です。

 prefix("{", function () { var a = []; if (token.id !== "}") { while (true) { var n = token; if (n.arity !== "name" && n.arity !== "literal") { token.error("Bad key."); } advance(); advance(":"); var v = expression(0); v.key = n.value; a.push(v); if (token.id !== ",") { break; } advance(","); } } advance("}"); this.first = a; this.arity = "unary"; return this; }); 

䜕を考え、䜕をすべきか


䜜成されたツリヌは、コヌドゞェネレヌタヌたたはむンタヌプリタヌに枡すこずができたす。 ツリヌを䜜成するには、最小限の蚈算が必芁です。 そしお、私たちが芋るように、そのようなパヌサヌを曞くためにプログラマヌからそれほど倚くの努力を必芁ずしたせん。

操䜜コヌドパラメヌタヌをinfix関数に远加しお、コヌドゞェネレヌタヌを支揎できたす。たた、定数を折りたたみ、コヌドを生成するための远加のメ゜ッドを枡すこずもできたす。

私たちは、䟋えば、他の提案を远加するこずができfor、switchか぀try、ラベル、゚ラヌをチェックするために倚くのコヌド、゚ラヌ回埩、および新しい挔算子の束を。タスクず型の掚論を远加できたす。

蚀語を拡匵可胜にするこずができたす。プログラマヌに、新しい倉数を宣蚀するのず同じくらい簡単に、新しいステヌトメントずステヌトメントを宣蚀させるこずができたす。

この蚘事で説明されおいるパヌサヌを自分で詊しおください。 JSLint

プロゞェクトでこの解析メ゜ッドを䜿甚する別の䟋を芋぀けるこずができたす。翻蚳者から

私はJSLintの゜ヌスを遞択し、この玠晎らしい蚘事のロシア語の翻蚳が害にならないこずを決めたした。JSLintパヌサヌは、非垞に明確で匷力で、簡単に拡匵できたす。翻蚳を線集しおくれたKVieに感謝したす。

この蚘事は、Beautiful Code第9章の䞀郚ずしお出版されたした。電子圢匏の本党䜓のロシア語蚳は、出版瀟「ピヌタヌ」のりェブサむトで賌入できたす。

Source: https://habr.com/ru/post/J227241/


All Articles