コンパイラの設計で、なぜ文法で再帰を残すべきなのですか?私はそれが無限再帰を引き起こす可能性があるからだと読んでいますが、正しい再帰文法にも当てはまりませんか?
コンパイラの設計で、なぜ文法で再帰を残すべきなのですか?私はそれが無限再帰を引き起こす可能性があるからだと読んでいますが、正しい再帰文法にも当てはまりませんか?
回答:
左再帰文法は必ずしも悪いことではありません。これらの文法は、LRパーサーの場合のように、スタックを使用して簡単に解析され、既に解析されたフレーズを追跡します。
CF文法の左再帰規則 は次の形式であることを思い出してください。
の要素Vとβの要素V ∪ Σ。(タプルのための完全な正式な定義を参照してください(V 、Σ 、R 、S )があります)。
通常、端末と非端末のシーケンスは、実際にある、とのために他のルールがありα αが右側に表示されませんが。
文法パーサーが(レクサーから)新しい端末を受信するたびに、この端末はスタックの上にプッシュされます。この操作はshiftと呼ばれます。
ルールの右側がスタックの最上部にある連続した要素のグループと一致するたびに、このグループは新しく一致したフレーズを表す単一の要素に置き換えられます。この置換はリダクションと呼ばれます。
正しい再帰文法を使用すると、縮約が発生するまでスタックが無限に大きくなり、解析の可能性が大幅に制限される可能性があります。ただし、左の再帰的なものは、コンパイラーがより早く(実際には、できるだけ早く)削減を生成できるようにします。詳細については、ウィキペディアのエントリを参照してください。
このルールを考慮してください:
example : 'a' | example 'b' ;
ここで、LLパーサー'b'
がこのルールのように一致しない文字列を一致させようとしていると考えてください。'a'
一致しないため、一致しようとしますexample 'b'
。しかし、そうするためには、一致する必要がありexample
ます...それはそもそもしようとしていたことです。トークンの同じストリームを同じルールに常に一致させようとするため、一致するかどうかを確認しようとすると、永久にスタックする可能性があります。
それを防ぐためには、右から解析する必要があります(私が見た限りでは非常にまれであり、代わりに正しい再帰を行うでしょう)、入れ子の許容量を人為的に制限するか、一致する必要があります再帰が開始する前のトークン。常にベースケースがあります(つまり、すべてのトークンが消費され、完全に一致するものはまだありません)。右再帰ルールはすでに3番目のルールを実行しているため、同じ問題はありません。
(私はこの質問が今ではかなり古いことを知っていますが、他の人が同じ質問を持っている場合...)
再帰降下パーサーのコンテキストで質問していますか?たとえば、文法のexpr:: = expr + term | term
場合、なぜ次のようになりますか(左再帰):
// expr:: = expr + term
expr() {
expr();
if (token == '+') {
getNextToken();
}
term();
}
問題はありますが、これはそうではありません(右再帰)。
// expr:: = term + expr
expr() {
term();
if (token == '+') {
getNextToken();
expr();
}
}
両方のバージョンがexpr()
自分自身を呼び出すようです。ただし、重要な違いはコンテキストです。つまり、再帰呼び出しが行われたときの現在のトークンです。
左の再帰的なケースでexpr()
は、同じトークンを使用して自身を継続的に呼び出しますが、進行はありません。正しい再帰の場合、への呼び出しterm()
に到達する前に、への呼び出しとPLUSトークンの入力の一部を消費しexpr()
ます。そのため、この時点で、再帰呼び出しはtermを呼び出してから、ifテストに再度到達する前に終了します。
たとえば、2 + 3 + 4の解析を検討します。左の再帰パーサーはexpr()
最初のトークンでスタック中に無限に呼び出しますが、右の再帰パーサーはexpr()
再度呼び出す前に「2 +」を消費します。expr()
「3 +」に一致する2番目の呼び出しと呼び出しexpr()
、残り4つだけ。4は用語に一致し、解析はを呼び出さずに終了しexpr()
ます。
Bisonマニュアルから:
「左再帰または右再帰のいずれかを使用して、あらゆる種類のシーケンスを定義できますが、スタック領域が制限されている任意の数の要素のシーケンスを解析できるため、常に左再帰を使用する必要があります。ルールを一度でも適用する前に、すべての要素をスタックにシフトする必要があるため、シーケンス内の要素数に比例します。これについての詳細は、Bison Parserアルゴリズムを参照してください。
http://www.gnu.org/software/bison/manual/html_node/Recursion.html
そのため、パーサーのアルゴリズムに依存しますが、他の回答で述べられているように、一部のパーサーは単に左再帰で動作しない場合があります