コンパむル。 5䞋方解析

ただ䞊向きの構文解析を行っおいたす。 他にどんなオプションがありたすか
バむ゜ンを脇に眮き、理論に戻りたす。

さらに投皿

  1. アむデア
  2. 化身
  3. ホリバヌ
  4. バックトラッキング

アむデア


䞊向き解析の䞀般的な考え方は次のずおりです。入力行を読み取り、䞀床に1文字ず぀スタックにシフトしたす。 文法芏則に䞀臎する組み合わせがスタックの最䞊郚で圢成されるずすぐに、それを1぀の非終端蚘号にたずめお続行したす。
この方法の重芁な特城は、ルヌルの折りたたみ䞭にすべおのコンポヌネントを手元に甚意し、必芁な構造でそれらからツリヌを構築したり、蚈算のために倖出先でも䜿甚できるこずです。

反察のアプロヌチは、文法の最初のシンボルの展開を開始し、次の入力シンボルに埓っお展開可胜なルヌルを遞択するこずです。
たずえば、文法に芏則がある堎合
 OP '{' OPS '}' //ブロック
     |  EXPR ';'  //匏
     |  'if' '' EXPR '' OP
     |  'if' '' EXPR '' OP 'else' OP
     |  'while' '' EXPR '' OP
     |  'exit' ';'  ;
テキストのさらに先にwhileがあるこずがわかりたす。その埌、 OP: 'while' '(' EXPR ')' OP ;ルヌルに埓っお展開しOP: 'while' '(' EXPR ')' OP ;

ストアマシンの堎合、このような解析は次のように実装できたす。スむヌプ䞭にアクションを実行するこずは無意味であるこずにすぐに泚意したす。展開されたルヌルの右郚分はただ読み取られおおらず、読み取られるかどうかはたったくわかりたせん。 展開するずきに、右の郚分のシンボルの䞋のスタックにダミヌ信号を配眮するこずができたす。「ルヌルが正垞に読み取られたした。アクションを実行できたす」。 しかし、この瞬間たでに、右偎党䜓が既にスタックから消去されおいたす アクションは䜕ず連携したすか

しかし、䞋向き構文解析の䞻な難しさはこれではなく、開発に適切なルヌルを遞択する方法です。
バむ゜ンには匷すぎる文法を思い出しおください。
 WORDS1 'a' 'i' 'l' |  S2 'a' 'l' 'e';
 S1 's';
 S2 's';

次の文字が's'ずき、どのようにWORDをアンラップしたすか
遞択方法は䞍明ですWORD䞡方の芏則は、正確に's'始たりたす。

化身


マシンが耇数の文字を先読みできるようにするこずができたす。レビュヌにより刀断するず、このアプロヌチは人気のLLパヌサヌゞェネレヌタヌであるANTLRで䜿甚されたした。
技術的な難しさは、スむヌプの開発が䟝存する新しいキャラクタヌごずに、遷移テヌブルの次元が増加するこずです。 したがっお、テキストから3文字スタックから1文字を読み取るN状態のオヌトマトンの非圧瞮テヌブルには、N・256 4芁玠数十ギガバむトが含たれたす。
したがっお、LLパヌシングの適甚先は、パヌサヌゞェネレヌタヌがテヌブルを効率的に圧瞮する胜力によっお正確に決定されたすバむ゜ンが258x14のテヌブルを7぀の短い1次元配列に圧瞮した方法を思い出しおください。

私が理解しおいる限り、すべおがすべおの人に問題がないため、長距離先読みのLRパヌサヌはそうではありたせん。長距離の怜玢が䞍十分であるこずに起因する競合はLRパヌサヌではたれです。 バむ゜ンが私たちの文法を認識できるように、2぀の同䞀の畳み蟌みの䞭から遞択するように頌たなくおも十分です。
 WORD 's' 'a' 'i' 'l' |  「s」「a」「l」「e」;
䞀方、LLパヌサヌでは䜕も倉曎されおいたせん。以前のように、䞡方のルヌルは's'文字に察応しおいたす。 したがっお、LL構文解析の文法を「適合」させるために、同じ非終端蚘号に察するさたざたな芏則の䞀般的な始たりが「補助非終端蚘号」に取り出されたす。
 WORD1 's' 'a' WORD;
 WORD 'i' 'l' |  'l' 'e';

このトリックは「巊因子分解」ず呌ばれたす。これは、「共通因子」が均等に開始されるルヌルから取り出されるかのようです。
これで、 WORD1スむヌプは䞀意であり、 WORD 2぀のスむヌプは異なる文字で始たりたす。

ANTLR miracle-squeezerが必芁なだけ先読みできるのに、なぜ因子分解に悩たされ、文法に意味のない非終端蚘号を生成するのですか
文法に戻る
 OP '{' OPS '}' //ブロック
     |  EXPR ';'  //匏
     |  'if' '' EXPR '' OP
     |  'if' '' EXPR '' OP 'else' OP
     |  'while' '' EXPR '' OP
     |  'exit' ';'  ;

次のifキャラクタヌをOPに展開する方法は この方法で開始できる2぀のルヌルがありたす。 そしお、それらの共通郚分'if' '(' EXPR ')' OPは任意の長さにするこずができるため、パヌサヌがどれだけ先を芋おも、これは圌を救いたせん。

LLが凊理できないもう1぀の問題は、巊再垰です。 垂堎取匕蚈算機の文法を芚えおおいおください
 EXPRNUM |  EXPR OP NUM;

EXPR䞡方の芏則はNUMから始たりたす。最初の芏則は明瀺的で、2番目の芏則は暗黙的です。 ただし、ルヌルから匕き出すこずができる䞀般的な「芁因」はありたせん。 NUMの長さに制限NUMないず仮定した堎合、先を芋越しおも問題が解決しないこずは明らかです。

文法を修正するために、その意味から始めたす。行の最初のNUMは既補の匏であり、他のすべおの匏には2぀のオペランドが含たれたす。
 EXPRNUM EXPR1;
 EXPR1|  OP NUM EXPR1;

巊因数分解ず巊再垰の排陀により、曖昧さのない文法をLL構文解析に適合させるこずができたすが、倚くの補助的な非終端蚘号を远加しおも、たったく意味がありたせん。
たずえば、 EXPR: EXPR OP NUM ;ルヌルの畳み蟌みでは、次のようになりEXPR: EXPR OP NUM ; 構文ツリヌノヌドを簡単に䜜成したした。巊の匕数-ここにありたす $1 。 正しい匕数はここにあり、 $3です。 EXPR1: OP NUM EXPR1 ;の展開でできるこずEXPR1: OP NUM EXPR1 ;  巊の匕数は既に展開されおおり、スタックから消去されおいたす 。 しかし、 EXPR1を手にしおいたす。正しい匕数の埌の郚分匏です。 䜕か良いもののようです

重芁なこずは、巊再垰はすべおの巊結合操䜜、およびそのほずんどにずっお自然なこずです。 したがっお、䞊蚘のような混乱は珍しい奜奇心ではなく、兞型的なケヌスです。

䞀方では、文法をLL圢匏に還元するこずは完党に圢匏的であり、魂のない鉄片に委ねるこずができたす。 䞀方、特定の文法ではなく、独自の文法に埓っお動䜜するオヌトマトンをデバッグする方法は
特に、デプロむメントのアクションを蚘述するこずは蚱可されたせん。すべお同じように、デプロむメントは蚭定したルヌルではなく、鉄片が蚭定した他のルヌルになるからです。 このようなパヌサヌが特定の解析ツリヌを生成できるず䟿利です。 これを行うには、特定の文法のルヌルをオヌトマトンが実際に機胜するルヌルず盞関させ、ツリヌを再構築しお、あるルヌルから別のルヌルに移動したノヌドを「元に戻す」必芁がありたす。

ホリバヌ


LLの利点に関する議論。 LRは䞀般に自動解析ず同じくらい叀いです。 どちらのアプロヌチにも独自の支持者がいたす。 䟋えば、私から個人的に深く尊敬されおいたニクラりス・ワヌスは、LL分析の積極的な謝眪者であり、Pascalの開発における蚭蚈目暙の1぀はLL1分析の可胜性でした。 テキストの次の文字を1぀だけ芋るオヌトマトン。 LLの「生きおいる蚀語」のほずんどは適合したせん。たずえば、Cでは、倉数たたは関数の宣蚀はintで開始でき、行を最埌たで読むたで区別できたせん。

利䟿性に関しお基本的に、誰もが圌が慣れおいる楜噚を賞賛し、珍しいこずを嫌いたす。
たずえば、「 ANTLR vs. バむ゜ン "
個人的な奜みの面では、LALR文法は構築ずデバッグがはるかに簡単だず思いたす。 欠点は、shift-reduceや恐ろしいreduce-reduceのようなやや䞍可解な゚ラヌに察凊する必芁があるこずです。 これらはパヌ゜ンを生成するずきにBisonがキャッチする゚ラヌであるため、゚ンドナヌザヌ゚クスペリ゚ンスには圱響したせんが、開発プロセスを少し面癜くするこずができたす。

その玛争におけるANTLRの利点には、解析自䜓の品質以倖のあらゆるものが含たれたす。䟿利な開発環境、異なる蚀語でのコヌド生成、ナヌザヌフレンドリヌな生成コヌドです。

ANTLRずテヌブルパヌサヌの最倧の違いは、䟿利に生成されるコヌドです。 実際、特殊な解析スタックの代わりに、呌び出しスタックが䜿甚され、各文法芏則の認識が関数呌び出し再垰芏則の堎合、再垰関数の堎合ずしお実装されたす。 したがっお、呌び出しスタックからデバッグする堎合、パヌサヌが珟圚の状態になった方法がすぐにわかりたす。 䞀方、バむ゜ンでは、思い出すように、状態間の遷移のデバッグ印刷をオンにし、遷移の理由を理解するためにマシンの印刷を確認する必芁がありたす。
テヌブルの前の再垰的実装の欠点も明らかです。コヌドの量がはるかに倚いため、メモリが消費されたす。 生成されたコヌドに「パッチ」を曞くこずは䞍可胜です。これは、文法が倉曎されるず倉曎され、さらに数十の関数が乗算されるためです。

バックトラッキング


スむヌプごずにルヌルを事前遞択するLLパヌサヌは、ダりンストリヌム解析の唯䞀のオプションではありたせん。 別のアむデアいく぀かの適切なルヌルがある堎合、 すべおを詊しおみたすが 、いく぀かはしたす。 たずえば、 fork()などの操䜜を実行できたす。珟圚の状態でパヌサヌクロヌンを䜜成し、各クロヌンにスキャンオプションの1぀を詊行させたす。 クロヌンが構文゚ラヌに遭遇するず、クロヌンは死にたす。 すべおのクロヌンが停止しおいる堎合、適切なスキャンオプションは1぀ではありたせん。入力テキストに構文゚ラヌがありたす。

正しいテキストの堎合、このアプロヌチは倚かれ少なかれ受け入れられたす。 しかし、゚ラヌ怜出には問題がありたす。 たず、怜出された゚ラヌのどれを印刷したすか それらのほずんどは、誀っお遞択されたスキャンの結果であり、プログラムテキストの゚ラヌではありたせん。 しかし、パヌサヌの芳点からは、すべおが完党に同等です。

第二に、分析が終わるこずはありたせん。巊再垰芏則に埓っおスむヌプを行うたびに、クロヌンの1぀はスむヌプ前ずたったく同じ状態になりたす。 そのため、各ステップでさらに1぀のたったく同じクロヌンが刀明し、無限に続きたす。 クロヌンの1぀が分析の終わりに達するず、残りのすべおを殺すこずができたす。 そしお、反察に、他のすべおのクロヌンが構文゚ラヌに遭遇しお死に、無駄なクロヌンだけが生き続けるのか

最埌に、あいたいな文法をどうするか LLおよびLRパヌサヌは、コンパむル時に競合を怜出したす。 ここでは、コンパむルなどはありたせんパヌサヌは、ほずんど生の文法、぀たり 倖出先で解釈したす。
そのため、同じテキストに察しお1぀の分析たたは別の分析のいずれかが埗られるこずが刀明する堎合がありたす。どのクロヌンが分析をより早く終了する時間があるかによっお異なりたす。

最埌の問題は最も独創的な方法で解決されたしたあいたいな構文解析の可胜性は文法の基本的な問題であり、それらの代わりに構文解析匏を䜿甚する必芁があるこずが発衚されたした。本質的には開発ルヌル間で厳密な優先順䜍が䞎えられるずいう点でのみ異なりたす。 たずえば、文法
OP: EXPR ';' | 'if' '(' EXPR ')' OP | 'if' '(' EXPR ')' OP 'else' OP ;
そしお
OP: EXPR ';' | 'if' '(' EXPR ')' OP 'else' OP | 'if' '(' EXPR ')' OP ;
-これはたったく同じ曖昧な文法であり、構文解析匏
OP: EXPR ';' / 'if' '(' EXPR ')' OP 'else' OP / 'if' '(' EXPR ')' OP
パヌサヌに「最初にif..elseを認識しようずし、倱敗した堎合のみif if..elseを認識しif..else 」ず明確に䌝えelse 。 そしお、衚珟
OP: EXPR ';' / 'if' '(' EXPR ')' OP / 'if' '(' EXPR ')' OP 'else' OP
は、「- else if if else 」を最初に認識するこずを意味するため、 elseはたったく認識されelse 。

ここで、可胜なすべおのスむヌプを同時にチェックする代わりに、優先床に埓っお順番にチェックしたす。 前のスキャンで゚ラヌが発生した堎合にのみ、次のスキャンに進みたす。 コメンテヌタヌが蚀及したリンクは、この解析方法の優れた比metaを提䟛したす。
怠zyな象に乗ったこずがありたすか 圌はゆっくり歩き、激しく揺れたす。 そしお、圌は道路を歩いおいたせんが、あらゆる方向にさたよい、面癜いず思いたすが、もしそれらが望たしいものず䞀臎しなければ、象の運転手は「いや、私たちはここにいたせん」ず蚀いたす。 だから、すべおの遞択肢を詊した埌、ゟりは「ここではない 」ず蚀わなかった時点で自分自身を芋぀けたす。 したがっお、パヌサヌコンビネヌタヌは、オプションのいずれかが機胜するたで、オプションのすべおの組み合わせも詊行したす。 そしお、各ロヌルバックの埌、圌らは再び同じ仕事をし始めたす。 <...>数行のプログラムはすぐに理解されたした。 すでに数秒で3行になりたす。 5行も埅たなかった。 芚えおおいおください、このようなパヌサヌコンビネヌタヌは、非垞に単玔な文法にのみ適しおいたす。

構文解析ルヌプを怜出し、蚱容可胜な䜜業時間を達成し、゚ラヌを特定するこずは残っおいたす。

ルヌプの方が簡単です。入力テキストを進めるこずなく、同じ状態に2回いる堎合、3回目に、そしお奜きなだけ同じ状態になりたす。 入力テキストの各䜍眮に぀いお、すでにその䜍眮に行ったすべおの状態のリストを保持する必芁があるこずがわかりたす。同じ状態に2床目になった堎合、「いいえ、それで十分です。もう行きたせん」ず蚀いたす。

無料のボヌナスずしお、「ええ、ありたした」ずいう目盛りだけでなく、以前の分析の結果スキャンが発生した/適合しなかったを芚えおいる堎合、「線圢」の動䜜時間が埗られたす。 最悪のシナリオは、テキストの各䜍眮にあるすべおの可胜な状態を蚪問する堎合です。 テキストの長さがN文字で、 文法でMルヌルの構文解析匏各非終端蚘号の代替開発オプションを含むの堎合、操䜜時間O  M * N が埗られたす。 ずる賢く目を现めお、 Mが定数であるふりをする堎合、そうです、時間は線圢です。 比范のために、各状態で事前定矩されたアクションを持぀埓来のLLパヌサヌずLRパヌサヌは、最悪の堎合O  M * N でたったく同じです。
 S|  T1 S;
 T1T2;
 T2T3;
 ...
 TM 't';
ここで、 't'各シフトの埌't' LRは't' -> TM -> ... -> T3 -> T2 -> T1 M畳み蟌みを実行したす。 LLは、各't' 「食べる」前に、 MスむヌプT1 -> T2 -> T3 -> ... -> TM -> 't'たす。
党䜓の問題は、平均的なケヌスが最悪のケヌスずどのように異なるかです。少なくずも「線圢象」は、同じ文法でLLよりも倚くの解析でスむヌプを実行したす。

メモリ消費のもう1぀の問題。 過去のスキャンの結果のM * Nを保存する必芁がありたす。これに加えお、入力テキストをメモリに完党に保存する必芁があるずいう事実に加えお、象は無限に前埌に実行する必芁があるためです。 これは、ストアで自動化されたパヌサヌがすでに読み取られたテキストに戻らないため、メモリ芁件はテキストのみではなく文法のみに䟝存するずいう事実にもかかわらずです。
誰もが「1バむトの歎史」を読みたしたか さらに、新しいコンパむラの最も自然なアプリケヌションの1぀は、保存されたメモリが重芁なすべおのコンパクトなプラットフォヌムをサポヌトするこずです。

そしお、構文゚ラヌの怜出に関しお。 実際にpackratskopidずいう名前を取埗した私たちの象は、1回のスキャンが行われなかったずきにテキストに間違いがあるず結論付けたした。 したがっお、゚ラヌが怜出される䜍眮の前に、゚ラヌ自䜓の堎所がはるかに先行する堎合がありたす。構文解析匏を想定したす
 DECLDECLVAR / DECLFN;
 DECLVARタむプID ';'  ;
 DECLFNタむプID '' ARGS '';
-Cの宣蚀の構文にリモヌトに䌌おいたす。
入力テキストにTYPE ID '!'ずしお解析できるシヌケンスが含たれおいる堎合 、その埌、構文゚ラヌはどの䜍眮にありたすか Packratは最初のスキャンをチェックしたすが、機胜したせん。パヌサヌはTYPEの先頭にロヌルバックし、2回目のスキャンを詊みたす。 圌女も合わない。 TYPE前に゚ラヌが怜出されるこずがTYPEたす。 TYPEがハヌフラむンのハヌドコア衚珟である堎合はどうなりたすか
少なくずも1回のスキャンで到達できた䞀番右のものを゚ラヌ䜍眮ずしお衚瀺するこずは論理的です。 パヌサヌがテキストが正垞に解析されるこずをただ期埅しおいた最埌の䜍眮。
゚ラヌのロヌカリれヌションがそのように実装されおいるpackrat実装があるず思いたす。

それだけです、それはシリヌズの最埌の投皿で、解析を扱っおいたす。
次回は、セマンティクスに移りたしょう。そうすれば、おもちゃの蚀語は本圓にプログラミング蚀語になりたす。

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


All Articles