文法規則を書きます
以前の研究では、我々はすでに文法のルールを知っての使用される文脈自由文法を 表します。文脈自由文法はセットである(また生産としても知られる)の置換規則が挙げられます。次のような形式での具体的な外観:
add -> mul | add + mul
mul -> pri | mul * pri
pri -> Id | Num | (add)
- 追加- > MUL | + MUL追加:アドオンプラス式で置き換えることができ 、乗算発現MUL、またはに置き換え プラス表現アドプラス 乗算発現MUL。
- MUL - > PRI | MUL * PRI : 乗算MUL式は置き換えることができ 、基本的な表現PRI 、またはに置き換え 乗算式がMULで乗算 ベース発現PRI。
- PRI - > ID |番号|(追加): ベースPRI式に置き換えることができ、デジタルリテラルのNum、または 変数ID、またはブラケットストリッププラス表現(アドオン)。
例の演算式導出、「* 2 5 + 3」の場合:
図の導出することにより、我々は明らかに、これらの二つの式が生成されたかを見ることができます。解析木の形成過程、実際には、AST。私たちは、リーフノード上の画像であることを観察したトークンのような、「*」、 「+」、およびテンキー数値リテラル。これらのリーフノードが呼び出されターミネータ、およびそれらの非リーフノードと呼ばれる非ターミナル、それがダウンして交換し続けることができますので、。
私たちは、頻繁に使用するBNF(BNF)の 文法のルールを記述すること。フロントと意味は同じですが、フォームの文言だけで同じではありません。
add ::= mul | add + mul
mul ::= pri | mul * pri
pri ::= Id | Num | (add)
時々 、私たちはと接触する拡張BNF(EBNF) 。それBNFは、に基づいて、正規表現に似た文言に参加しました。
add -> mul (+ mul)*
標準のBNFの文言との文言は、同等に、しかし、より簡潔です。同時に、我々は再び説明することができます:文脈自由文法は正規文法は、より多くのことを行うことができるよりも、定期的な文法を含んでいます。
どのように優先順位を確保するには?
私たちは、電卓がすでにによって知られている時に行う乗算ルールに導出規則のほか、というこのアプローチ性を保証AST乗算ノードが下位加算ノードになりますが、乗算の計算は加算演算よりも優先されることを保証します。
したがって、我々は、必要があり、上部に加えて(> = <)操作をリレーショナル、関係演算の上部に論理演算(AND、OR) 。
exp -> or | or = exp
or -> and | or || and
and -> equal | and && equal
equal -> rel | equal == rel | equal != rel
rel -> add | rel > add | rel < add | rel >= add | rel <= add
add -> mul | add + mul | add - mul
mul -> pri | mul * pri | mul / pri
:ローからハイへの優先代入演算、論理演算(OR)、論理演算(AND)、等しい(等しい)と比較し、比較のサイズ(REL)、加算器(ADD)、乗算(MUL)、式の基( PRI)。
優先順位の変更が発生した後に実際には、しかし、状況は括弧など、より複雑です。
実際には、我々は最も低いレベルである最高の優先的表現(PRI)とここで、式を包むためのブラケット、表現再帰的に参照すること。この場合、限り時間の括弧解析式での出会いなど、あなたはこれが最優先であることを知っています。だから、優先順位の変更を達成するであろう:
pri -> Id | Literal | (exp)
これまでのところ、我々は表現の優先順位の問題を解決することができました。
結合を保証するには?
我々は前に計算された「2 + 3 + 4」の表現のための計算は「3 + 4」を計算することであるため、書き込みバインディング式の添加が、間違っている電卓を、書く前「7 + 2」は、計算順序が結合残りません。我々は間違って使用し、右再帰的な文法を、そして最終的には、次の図で間違ったAST、生成します。
同じ優先順位の演算子はされて右に左または左に右が計算されると呼ばれる結合します。そのような添加物として最も一般的な算術演算、減算は左結合で、「」記号は、左結合されています。
したがって、我々は、オペレータのために必要とされる連想左または右結合決定再帰的に長期的な位置。右結合演算子、右側に再帰的用語、左側になるように再帰的なアイテム、オペレータの結合左ため。
そのため、さらには、このように記述する必要があります。
add -> mul | add + mul
質問があるので、ほとんどの二項演算子が左結合され、それが顔左再帰の問題に持っていないでしょうか?我々は、左再帰的な文法を書き換えることができ、この問題を解決するために、心配しないでください。
左再帰の問題を解消
標準的な方法を使用して、左再帰を排除し、可能である 左再帰的な文法非左再帰的な文法を書き換えます。
:、私たちは今のように書き換える - 「> MUL +追加」正規表現のほかには、例えば、オリジナルの文法があります
add -> mul add'
add' -> + mul add' | ε
- [追加] - > [MUL addは「:乗算の追加に続いて、加算器に置き換えることができます」
- 追加「 - > + MUL追加」| [イプシロン]:追加「+は、空のセットまたは乗算に置き換えてもよいです。
文法、[イプシロン]が ある空のセットを意味しています。
当社は、取得したいと考えています:
言い換えれば、ときマッチプラス表現、最初に一致乗算式は、(基本的な表現を続けるために押し下げることができる)、そして背中の後ろにプラスと乗算表現を参照してください。
しかし、まだ問題があります。起因する規則は、右再帰(プラス右の記号)である」を追加は、標準の再帰的なアルゴリズムが減少すれば、我々は、オペレータエラーの結合性のような話が発生します従います。私たちは、ASTを楽しみにするツリーの右側で、その結合は正しいです。解決策はありますか?
答えはイエスです。我々は慎重に上記の派生文法規則のプロセスを分析します。最初のステップは、最後まで追加「導出規則に従ってすべての後に、導出の規則に従ってのみ追加することです。アスタリスクは、繰り返し数と+を表す可能にすることによって、EBNFの方法、すなわちで発現した場合、二つの規則上記一つに結合することができます。
add -> mul (+ mul)*
最適化の考え方に、私たちはアルゴリズムを記述します。(+ MUL)*このセクションでは、我々は実際に書くことができるループではなく、時間の再帰呼び出しを。
mul();
while(next token is +){
mul()
createAddNode
}
再帰関数は、そこにある研究では、と呼ばれる概念末尾再帰、最後の文の末尾再帰関数である再帰的(古典的な末尾再帰は、フィボナッチ数である)自分自身を呼び出します。
通常コンパイラに末尾再帰ループ上記擬似コードの使用の原理は、同じです。再帰呼び出しに関しては、ループ低オーバーヘッド、システムリソース、ように末尾再帰ループにも、コンパイラの最適化技術。
だから、プラスの発現のために、我々は、コードを変更することができます。
private SimpleASTNode additive(TokenReader tokens) throws Exception {
SimpleASTNode child1 = multiplicative(tokens); //应用add规则
SimpleASTNode node = child1;
if (child1 != null) {
while (true) { //循环应用add'
Token token = tokens.peek();
if (token != null && (token.getType() == TokenType.Plus || token.getType() == TokenType.Minus)) {
token = tokens.read(); //读出加号
SimpleASTNode child2 = multiplicative(tokens); //计算下级节点
node = new SimpleASTNode(ASTNodeType.Additive, token.getText());
node.addChild(child1); //注意,新节点在顶层,保证正确的结合性
node.addChild(child2);
child1 = node;
} else {
break;
}
}
}
return node;
}
このように、我々は入れて左再帰の問題は解決しました。
概要
- 優先順位は 決定に派生文法のレベルであり、優先順位を下げ、導出する最初の試み。
- 結合性は左または右再帰的に関連すると再帰的で、再帰は左連想左右の再帰的につながった適切な組み合わせを引き起こしました。
- 左再帰は 、文法のルールを書き換え構文と書き換えることによって回避することができ、再帰ループの代わりに権利を使用するために、私たちに影響を与え、単純なEBNF形式、として表すことができます。