シェンマは「通訳モード」ですか?
最初に「GOF」を開いて、定義を見てみましょう。
言語が与えられた場合、その文法の表現を定義し、この表現を使用して言語の文を解釈する通訳を定義します。
最初の前に、私はまだいくつかの概念を普及させる必要があります:
抽象的構文ツリー:
通訳パターンは、抽象的な構文ツリーを作成する方法を説明していません。文法分析は含まれません。抽象的な構文ツリーは、テーブル駆動型の構文分析プログラムによって完了するか、手書き(通常は再帰的な降順)構文分析プログラムによって作成されるか、クライアントが直接提供することができます。
パーサー:
これは、クライアントコールに必要な式を説明し、解析後に抽象的な構文ツリーを形成するプログラムを指します。
通訳者:
抽象的な構文ツリーを説明し、各ノードの対応する関数を実行するプログラムを指します。
通訳パターンを使用するための重要な前提条件は、文法としても知られる一連の文法ルールを定義することです。この文法のルールが単純か複雑かに関係なく、対応する関数を分析および実行することであるため、これらのルールを含める必要があります。
構造図と通訳モードの説明を見てみましょう。
抽象化:通訳者のインターフェイスを定義し、インタープリターの解釈操作に同意します。
ターミナルエクスペレーション:ターミナルエクスペレーションは、構文ルールでターミネーターに関連する操作を実装するために使用され、他の通訳は含まれなくなりました。組み合わせパターンを使用して抽象的な構文ツリーを構築する場合、組み合わせパターンの葉オブジェクトと同等であり、複数のターミネーター通訳が存在する可能性があります。
非ターミナルエクスペレーション:構文ルールで非末端関連操作を実装するために使用される非末端通訳。通常、インタープリターは構文ルールに対応し、他の通訳を含めることができます。構成パターンを使用して抽象的構文ツリーを構築する場合、構成パターンの組み合わせオブジェクトに相当します。複数の非末端通訳者が存在する可能性があります。
コンテキスト:コンテキストには、通常、各通訳者またはパブリック機能に必要なデータが含まれます。
クライアント:クライアントは、インタープリターを使用するクライアントを指します。通常、言語の構文に従って作成された式は、インタープリターオブジェクトによって記述された抽象的な構文ツリーに変換され、説明操作が呼び出されます。
ここでは、XMLの例を使用して、通訳パターンを理解します。
まず、表現のための簡単な文法を設計する必要があります。汎用のために、ルートを使用してルート要素、ABCなどを表して要素を表します。単純なXMLは次のとおりです。
コードコピーは次のとおりです。
<?xmlバージョン= "1.0" encoding = "utf-8">
<root id = "rootid">
<a>
<b>
<c name = "testc"> 12345 </c>
<d id = "1"> d1 </d>
<d id = "2"> d2 </d>
<d id = "3"> d3 </d>
<d id = "4"> d4 </d>
</b>
</a>
</root>
条約表現の文法は次のとおりです。
1.単一の要素の値を取得:ルート要素から、値を取得する要素までずっと開始します。要素の中央は「/」で分離され、no "/"はルート要素の前に追加されます。たとえば、「ルート/a/b/c」という式は、ルート要素、要素A、要素B、および要素cの下で要素cの値を取得することを意味します。
2。単一の要素の属性の値を取得します。もちろん、複数の属性があります。値を取得するための属性は、式の最後の要素の属性でなければなりません。追加 "。"最後の要素の後、属性の名前を追加します。たとえば、「ルート/a/b/c。」という式は、ルート要素、要素A、要素B、要素cの名前属性の値を取得することを意味します。
3.同じ要素名の値を取得します。もちろん、複数の要素があります。値を取得する要素は、式の最後の要素であり、最後の要素の後に「$」を追加する必要があります。たとえば、「ルート/a/b/d $」という式は、ルート要素、A要素、およびb要素の下で、複数のd要素の値の収集を表します。
4.同じ要素名で属性の値を取得します。もちろん、複数があります。属性値を取得する要素は式の最後の要素でなければならず、最後の要素の後に「$」を追加します。たとえば、「ルート/a/b/d $ .id $」という式は、ルート要素、A要素、およびB要素の下で、複数のd要素ID属性の値の収集を表します。
抽象的な構文ツリーに対応する上記のXML、および可能な構造を図に示します。
以下の特定のコードを見てみましょう。
1。コンテキストを定義します。
コードコピーは次のとおりです。
/**
*コンテキスト、通訳者が必要とするいくつかのグローバル情報を含めるために使用されます
* @param {string} filepathName [読む必要があるXMLのパスと名前]
*/
関数コンテキスト(filepathName){
//前の処理要素
this.preele = null;
// XMLドキュメントオブジェクト
this.document = xmlutil.getRoot(filepathName);
}
context.prototype = {
//コンテキストを再活性化します
Reinit:function(){
this.preele = null;
}、
/**
*各式の公的使用方法
*親要素の名前と現在の要素に従って現在の要素を取得します
* @param {要素} pele [親要素]
* @param {string} elename [現在の要素名]
* @return {要素| null} [現在の要素が見つかりました]
*/
getNowele:function(pele、elename){
var tempnodelist = pele.childnodes;
var Nowele;
for(var i = 0、len = tempnodelist.length; i <len; i ++){
if((nowele = tempnodelist [i])。nodeType === 1)
if(nowele.nodename === elename)
Noweleを返します。
}
nullを返します。
}、
getPreele:function(){
this.preeleを返します。
}、
SetPreele:function(preele){
this.preele = preele;
}、
getDocument:function(){
this.documentを返します。
}
};
コンテキストでは、ツールオブジェクトXmlutilを使用してXmldomを取得しました。以下では、DOM3のドンペイザーを使用しています。一部のブラウザはそれをサポートしていない場合があります。ベースブラウザを使用してください:
コードコピーは次のとおりです。
//ツールオブジェクト
// XMLを解析して、対応するドキュメントオブジェクトを取得します
var xmlutil = {
getRoot:function(filepathName){
var parser = new domparser();
var xmldom = parser.parsefromstring( '<root id = "rootid"> <a> <b> <c name = "testc"> 12345 </c> <d id = "1"> d1 </d> <d id = "2"> d2 </d> <d id = "3"> d3 </d> <d id = "4"> d4 </d> </b> </a> </root> '、' text/xml ');
xmldomを返します。
}
};
これが通訳のコードです:
コードコピーは次のとおりです。
/**
*要素は、中間要素を解釈および実行するために非ターミナルに対応する通訳として使用されます
* @param {string} elename [要素の名前]
*/
関数ElementExpression(ELENAME){
this.ELES = [];
this.ELENAME = ELENAME;
}
lementExpression.prototype = {
addele:function(elename){
this.eles.push(elename);
trueを返します。
}、
removele:function(ele){
for(var i = 0、len = this.eles.length; i <len; i ++){
if(ELE === this.ELES [i])
this.eles.splice(i-、1);
}
trueを返します。
}、
解釈:function(context){
//まず親要素としてコンテキストの現在の要素を取り出します
//現在の要素名に対応するXML要素を見つけて、コンテキストに戻します
var pele = context.getPreele();
if(!pele){
//ルート要素が取得されることを示します
context.setPreele(context.getDocument()。documentLement);
} それ以外 {
//検索する親要素の名前と要素に基づいて現在の要素を取得します
var nowele = context.getnowele(pele、this.elename);
//現在検索されている要素をコンテキストに入れます
context.setpreele(nowele);
}
var ss;
//子要素の解釈方法を呼び出すループ
for(var i = 0、len = this.eles.length; i <len; i ++){
ss = this.ELES [i] .Interpret(context);
}
//最後の通訳の説明結果を返します。一般に、最後の通訳はターミネーター通訳です。
ssを返します。
}
};
/**
*要素はターミネーターに対応する通訳として使用されます
* @param {string} name [要素の名前]
*/
関数ElementTerminalExpression(name){
this.ELENAME = name;
}
elementterminalExpression.prototype = {
解釈:function(context){
var pele = context.getPreele();
var ele = null;
if(!pele){
ele = context.getDocument()。documentLement;
} それ以外 {
ele = context.getnowele(pele、this.elename);
Context.SetPreele(ELE);
}
//要素の値を取得します
return ele.firstchild.nodevalue;
}
};
/**
*属性はターミネーターに対応する通訳として使用されます
* @param {string} propname [属性の名前]
*/
関数PropertyTerminalExpression(Propname){
this.propname = propname;
}
PropertyTerminalExpression.Prototype = {
解釈:function(context){
//最後の要素属性の値を直接取得します
return Context.getPreele()。getAttribute(this.PropName);
}
};
最初に、通訳を使用して単一の要素の値を取得する方法を見てみましょう。
コードコピーは次のとおりです。
void function(){
var c = new Context();
//複数のD要素の値、つまり次の式の値を取得したい:「root/a/b/c」
//最初に、インタープリター用の抽象的構文ツリーを構築する必要があります
var root = new ElementExpression( 'root');
var aeLe = new ElementExpression( 'A');
var bele = new ElementExpression( 'B');
var cele = new Element TerminalExpression( 'C');
//組み合わせ
root.addele(aele);
aele.addele(bele);
bele.addele(cele);
console.log( 'cの値は=' + root.interpret(c));
}();
出力:Cの値は= 12345です
次に、上記のコードを使用して、単一の要素の属性の値を取得します。
コードコピーは次のとおりです。
void function(){
var c = new Context();
// D要素のID属性、つまり次の式の値を取得したい:「A/B/C.NAME」
//今回はCが終わっていないため、CをElementExpressionに変更する必要があります
var root = new ElementExpression( 'root');
var aeLe = new ElementExpression( 'A');
var bele = new ElementExpression( 'B');
var cele = new ElementExpression( 'C');
var prop = new Property TerminalExpression( 'name');
//組み合わせ
root.addele(aele);
aele.addele(bele);
bele.addele(cele);
cele.addele(prop);
console.log( 'cのプロパティ名値は=' + root.interpret(c));
//同じコンテキストを使用して継続的に解析する場合は、コンテキストオブジェクトを再現する必要があります
//たとえば、属性名の値を再び連続して再度獲得する必要があります。もちろん、要素を再結合することができます
// reparse、同じコンテキストが使用されている限り、コンテキストオブジェクトを再初期化する必要があります
c.reinit();
console.log( 'reget cのプロパティ名値は=' + root.interpret(c));
}();
出力:cの属性名値は= testc cの属性名値を取得= testc
説明する:
1。インタープリターモード機能:
インタープリターパターンは、インタープリターオブジェクトを使用して、対応する構文ルールを表現および処理します。一般に、インタープリターは構文ルールを処理します。理論的には、構文に準拠した式がインタープレーターオブジェクトで表され、抽象的な構文ツリーを形成できる限り、インタープリターパターンを使用して処理できます。
2。構文のルールと通訳
構文ルールと通訳者の間には対応があります。一般に、インタープリターは構文ルールを処理しますが、反対は真実ではありません。構文ルールには、複数の解釈と処理があります。つまり、構文ルールは複数の通訳に対応できます。
3。コンテキストの共通性
コンテキストは、インタープリターモードで非常に重要な役割を果たします。コンテキストがすべての通訳者に渡されるためです。したがって、通訳者の状態は、コンテキストで保存およびアクセスできます。たとえば、前の通訳はコンテキストにいくつかのデータを保存でき、後者のインタープリターはこれらの値を取得できます。
さらに、インタープリターの外側の一部のデータはコンテキストに渡すことができますが、インタープリターはそれを必要とし、いくつかのグローバルなパブリックデータが必要です。
コンテキストにも関数があります。つまり、各インタープリターオブジェクトで呼び出すことができる共通関数を取得するために継承を使用するのではなく、オブジェクトの組み合わせと同様に、すべてのインタープリターオブジェクトの共通機能を提供できるということです。
4。抽象的な構文ツリーを構築する人
前の例では、クライアント側に抽象的な構文ツリーを手動で構築することは非常に厄介ですが、インタープリターモードでは、関数のこの部分は関与しておらず、構築された抽象的な構文ツリーの解釈と処理のみを担当します。式を抽象的構文ツリーに変換するためのパーサーを提供できることを紹介します。
別の問題があります。つまり、構文ルールは複数のインタープリターオブジェクトに対応できます。つまり、同じ要素を複数のインタープリターオブジェクトに変換できます。つまり、同じ式が不要な抽象的な構文ツリーを形成することができます。
5。操作の説明を誰が担当します
抽象的な構文ツリーが定義されている限り、通訳者は解釈と実行に責任を負う必要があります。異なる構文ルールがありますが、インタープリターは、実行構文ルールを解釈するために使用するインタープリターオブジェクトを選択する責任を負いません。抽象的な構文ツリーを構築するときに、インタープリターを選択する機能が完了します。
6.通訳モードの呼び出し順序
1)コンテキストオブジェクトを作成します
2)複数のインタープリターオブジェクトを作成し、抽象的な構文ツリーを組み合わせる
3)通訳オブジェクトの解釈操作を呼び出します
3.1)コンテキストを介して通訳の状態を保存およびアクセスします。
非ターミネーターインタープリターオブジェクトの場合、含まれるサブインタープレーターオブジェクトを再帰的に呼び出します。
通訳パターンの本質: *実装を個別に、実行を解釈 *
インタープリターモジュールは、インタープリターオブジェクトを使用して構文ルールを処理して複雑な関数を分離します。次に、実行する必要がある関数を選択し、これらの関数を解釈および実行する必要がある抽象的な構文ツリーに結合します。次に、抽象的構文ツリーに従って実行を解釈して、対応する関数を実装します。
表面的には、インタープリターモードは、通常は使用していないカスタム構文の処理に焦点を当てています。しかし、本質的に、通訳モードのアイデアは分離、カプセル化、簡素化であり、多くのモードと同じです。
たとえば、インタープリターモードを使用して、状態モードの関数をシミュレートできます。インタープリターモードによって処理される構文が1つの状態マークのみに簡素化されている場合、インタープリターは状態の処理オブジェクトと見なされます。状態を表す同じ構文の場合、多くの未使用の通訳者が存在する場合があります。つまり、処理状態が異なる多くのオブジェクトがあります。抽象的な構文ツリーを作成する場合、状態マークに基づいて対応するインタープリターを作成することが簡素化され、ツリーを構築する必要はありません。
同様に、インタープリターモードは、ポリシーモードの実装機能、デコレータモードの機能など、特にデコレータモードの関数をシミュレートするプロセス、および抽象的な構文ツリーを構築するプロセスをシミュレートすることができます。
通常、インタープリターモードは高速ではなく(ほとんど非常に遅い)、エラーのデバッグは困難です(パート1:デバッグは困難ですが、実際にエラーの可能性を減らします)が、その利点は明らかです。モジュール間のインターフェイスの複雑さを効果的に制御できます。実行頻度が低いがコード頻度が高く、非常に多様である関数の場合、通訳者はモードに非常に適しています。さらに、通訳者にはもう1つの目立たない利点があります。つまり、便利なクロスランゲージとクロスプラットフォームになる可能性があります。
インタープリターモードの利点と短所:
アドバンテージ:
1.簡単に実装できます
インタープリターモードでは、構文ルールは、実行を解釈するためのインタープリターオブジェクトで解釈されます。インタープリターの実装では、機能は比較的単純になります。この構文ルールの実装を検討するだけで、他に何も心配する必要はありません。 2。新しい構文を簡単に拡張できます
これはまさに、新しい構文を拡張することが非常に簡単であるという構文ルールの責任をインタープリターオブジェクトがどのように責任を負うかのためです。新しい構文は拡張されており、抽象的な構文ツリーを作成するときに、対応するインタープリターオブジェクトを作成し、この新しいインタープリターオブジェクトを使用するだけです。
欠点:
複雑な構文には適していません
構文が特に複雑な場合、インタープリターパターンに必要な抽象的な構文ツリーを構築する作業は非常に困難であり、複数の抽象的な構文ツリーを構築することが可能です。したがって、インタープリターパターンは複雑な構文には適していません。構文アナライザーまたはコンパイラジェネレーターを使用する方が良いかもしれません。
いつ使用するのですか?
解釈して実行する必要がある言語があり、その言語の文を抽象的構文ツリーとして表すことができる場合、インタープリターパターンの使用を検討できます。
インタープリターモードを使用する場合、考慮する必要がある他の2つの機能があります。 1つは、構文が比較的単純でなければならないことです。あまりにも責任のある構文は、インタープリターモードの使用には適していません。もう1つは、効率の要件がそれほど高くなく、効率の要件が非常に高く、使用には適していないことです。
以前の紹介は、単一の要素の値と単一要素属性の値を取得する方法でした。複数の要素の値と複数の要素の名前の値を取得する方法を見てみましょう。以前のテストはすべて、人為的に組み合わされた抽象的構文ツリーです。また、以下の単純なパーサーを実装して、上記で定義された構文に準拠する式を変換して、上記のインタープリターの抽象的構文ツリーに変換します。コードを直接投稿しました。
コードコピーは次のとおりです。
//複数の要素または属性の値を読み取ります
(関数 () {
/**
*コンテキスト、通訳者が必要とするいくつかのグローバル情報を含めるために使用されます
* @param {string} filepathName [読む必要があるXMLのパスと名前]
*/
関数コンテキスト(filepathName){
//前に処理された複数の要素
this.preeLes = [];
// XMLドキュメントオブジェクト
this.document = xmlutil.getRoot(filepathName);
}
context.prototype = {
//コンテキストを再活性化します
Reinit:function(){
this.preeLes = [];
}、
/**
*各式の公的使用方法
*親要素の名前と現在の要素に従って現在の要素を取得します
* @param {要素} pele [親要素]
* @param {string} elename [現在の要素名]
* @return {要素| null} [現在の要素が見つかりました]
*/
getnoweles:function(pele、elename){
var Elements = [];
var tempnodelist = pele.childnodes;
var Nowele;
for(var i = 0、len = tempnodelist.length; i <len; i ++){
if((nowele = tempnodelist [i])。nodeType === 1){
if(nowele.nodename === elename){
elements.push(nowele);
}
}
}
返品要素。
}、
getPreeles:function(){
this.preeleを返します。
}、
setPreeLES:function(noweles){
this.preeLes = noweles;
}、
getDocument:function(){
this.documentを返します。
}
};
//ツールオブジェクト
// XMLを解析して、対応するドキュメントオブジェクトを取得します
var xmlutil = {
getRoot:function(filepathName){
var parser = new domparser();
var xmldom = parser.parsefromstring( '<root id = "rootid"> <a> <b> <c name = "testc"> 12345 </c> <d id = "1"> d1 </d> <d id = "2"> d2 </d> <d id = "3"> d3 </d> <d id = "4"> d4 </d> </b> </a> </root> '、' text/xml ');
xmldomを返します。
}
};
/**
*要素は、中間要素を解釈および実行するために非ターミナルに対応する通訳として使用されます
* @param {string} elename [要素の名前]
*/
関数ElementExpression(ELENAME){
this.ELES = [];
this.ELENAME = ELENAME;
}
lementExpression.prototype = {
addele:function(elename){
this.eles.push(elename);
trueを返します。
}、
removele:function(ele){
for(var i = 0、len = this.eles.length; i <len; i ++){
if(ELE === this.ELES [i]){
this.eles.splice(i-、1);
}
}
trueを返します。
}、
解釈:function(context){
//まず親要素としてコンテキストの現在の要素を取り出します
//現在の要素名に対応するXML要素を見つけて、コンテキストに戻します
var peles = context.getPreeles();
var ele = null;
var noweles = [];
if(!peles.length){
//ルート要素が取得されることを示します
ele = context.getDocument()。documentLement;
peles.push(ele);
context.setpreeles(peles);
} それ以外 {
var Tempele;
for(var i = 0、len = peles.length; i <len; i ++){
Tempele = Peles [i];
noweLes = noweLes.concat(context.getNoweles(tempele、this.Elename));
//見つけた場合は停止します
if(noweLes.length)break;
}
context.setPreeLes([noweles [0]]);
}
var ss;
//子要素の解釈方法を呼び出すループ
for(var i = 0、len = this.eles.length; i <len; i ++){
ss = this.ELES [i] .Interpret(context);
}
ssを返します。
}
};
/**
*要素はターミネーターに対応する通訳として使用されます
* @param {string} name [要素の名前]
*/
関数ElementTerminalExpression(name){
this.ELENAME = name;
}
elementterminalExpression.prototype = {
解釈:function(context){
var peles = context.getPreeles();
var ele = null;
if(!peles.length){
ele = context.getDocument()。documentLement;
} それ以外 {
ele = context.getnoweles(peles [0]、this.elename)[0];
}
//要素の値を取得します
return ele.firstchild.nodevalue;
}
};
/**
*属性はターミネーターに対応する通訳として使用されます
* @param {string} propname [属性の名前]
*/
関数PropertyTerminalExpression(Propname){
this.propname = propname;
}
PropertyTerminalExpression.Prototype = {
解釈:function(context){
//最後の要素属性の値を直接取得します
return Context.getPreeLes()[0] .getAttribute(this.propname);
}
};
/**
*複数の属性がターミネーターに対応する通訳として使用されます
* @param {string} propname [属性の名前]
*/
関数propertySterminalExpression(propname){
this.propname = propname;
}
propertySterminalExpression.prototype = {
解釈:function(context){
var eles = context.getPreeLes();
var ss = [];
for(var i = 0、len = eles.length; i <len; i ++){
ss.push(eles [i] .getattribute(this.propname));
}
ssを返します。
}
};
/**
*ターミネーターとして複数の要素を持つ解釈処理オブジェクト
* @param {[type]} name [description]
*/
関数ElementsterminalExpression(name){
this.ELENAME = name;
}
ElementsterminalExpression.prototype = {
解釈:function(context){
var peles = context.getPreeles();
var noweles = [];
for(var i = 0、len = peles.length; i <len; i ++){
noweLes = noweLes.concat(context.getNoweles(peles [i]、this.elename));
}
var ss = [];
for(i = 0、len = noweles.length; i <len; i ++){
ss.push(noweles [i] .firstchild.nodevalue);
}
ssを返します。
}
};
/**
*複数の要素は非端子として解釈されます
*/
関数elementsexpression(name){
this.ELENAME = name;
this.ELES = [];
}
elementsexpression.prototype = {
解釈:function(context){
var peles = context.getPreeles();
var noweles = [];
for(var i = 0、len = peles.length; i <len; i ++){
noweLes = noweLes.concat(context.getNoweles(peles [i]、this.elename));
}
Context.SetPreeLes(Noweles);
var ss;
for(i = 0、len = this.eles.length; i <len; i ++){
ss = this.ELES [i] .Interpret(context);
}
ssを返します。
}、
addele:function(ele){
this.ELES.Push(ELE);
trueを返します。
}、
removele:function(ele){
for(var i = 0、len = this.eles.length; i <len; i ++){
if(ELE === this.ELES [i]){
this.eles.splice(i-、1);
}
}
trueを返します。
}
};
void function(){
// "root/a/b/d $"
var c = new Context( 'interpreter.xml');
var root = new ElementExpression( 'root');
var aeLe = new ElementExpression( 'A');
var bele = new ElementExpression( 'B');
var dele = new ElementsterminalExpression( 'd');
root.addele(aele);
aele.addele(bele);
bele.addele(dele);
var ss = root.interpret(c);
for(var i = 0、len = ss.length; i <len; i ++){
console.log( 'd値は=' + ss [i]);
}
}();
void function(){
// a/b/d $ .id $
var c = new Context( 'interpreter.xml');
var root = new ElementExpression( 'root');
var aeLe = new ElementExpression( 'A');
var bele = new ElementExpression( 'B');
var dele = new Elementsexpression( 'd');
var prop = new PropertySterminalExpression( 'id');
root.addele(aele);
aele.addele(bele);
bele.addele(dele);
dele.addele(prop);
var ss = root.interpret(c);
for(var i = 0、len = ss.length; i <len; i ++){
console.log( 'dプロパティID値は=' + ss [i]);
}
}();
//パーサー
/**
*パーサーの実装アイデア
* 1.クライアントが渡す式を分解し、それらを1つずつ要素に分解し、対応する分析モデルを使用して、この要素に関する情報をカプセル化します。
* 2。各要素の情報に基づいて、対応するパーサーオブジェクトに変換します。
* 3。抽象的な構文ツリーを取得するために、これらのパーサーオブジェクトを組み合わせます。
*
* 1と2をマージし、要素を直接分解して対応するパーサーオブジェクトに変換してみませんか?
* 1。機能的な分離、メソッドの関数を複雑すぎてはしないでください。
* 2。将来の変更と拡張のために、構文は単純になるため、パーサーオブジェクトに変換する際に考慮すべきことはほとんどなく、直接変換することは難しくありませんが、構文が複雑な場合、直接変換は非常に乱雑になります。
*/
/**
*解析された各要素の対応する属性をカプセル化するために使用されます
*/
関数parsermodel(){
//単一の値かどうか
this.singlevalue;
//それが属性であるかどうか、属性または要素のいずれか
this.propertyvalue;
//終了するかどうか
this.end;
}
parsermodel.prototype = {
ISEND:function(){
this.end;
}、
Setend:function(end){
this.end = end;
}、
IssingLalue:function(){
this.singlevalueを返します。
}、
setSingleValue:function(oneValue){
this.singlevalue = oneValue;
}、
ispropertyvalue:function(){
this.propertyvalueを返します。
}、
setPropertyValue:function(propertyValue){
this.propertyvalue = propertyValue;
}
};
var parser = function(){
var backlash = '/';
var dot = '。';
var Dollar = '$';
//分解の順序に従って解析する必要がある要素の名前を記録します
var listele = null;
// Start the first step --------------------------------------------------------------------------------------------------------------------------
/**
*文字列式を渡してから、それを解析して抽象的構文ツリーに結合します
* @param {string} expr [値を取るために文字列式を説明]
* @return {object} [対応する要約構文ツリー]
*/
関数parsemappath(expr){
//最初に「/」に従って文字列を分割します
var tokenizer = expr.split(backlash);
//分解された値の表
var mappath = {};
var onepath、elename、propname;
var dotindex = -1;
for(var i = 0、len = tokenizer.length; i <len; i ++){
OnePath = Tokenizer [i];
if(tokenizer [i + 1]){
//別の値があります。つまり、これは最後の要素ではないことを意味します
//現在の構文によれば、属性は最後にある必要があるため、属性ではありません。
SetParsePath(false、onepath、false、mappath);
} それ以外 {
//終わりです
dotindex = onepath.indexof(dot);
if(dotindex> = 0){
//属性の値を取得することを意味するため、「。」に従って分割することを意味します。
//最初のものは要素名、2番目のものは属性名です
ELENAME = OnePath.Substring(0、dotindex);
propname = onepath.substring(dotindex + 1);
//プロパティの前の要素は、当然のことながら最後の要素ではなく、プロパティでもありません
setParsePath(false、elename、false、mappath);
//属性を設定します。現在の構文定義によれば、属性は最後のものにすぎません。
setParsePath(true、propname、true、mappath);
} それ以外 {
//命令は要素の値、および最後の要素の値と見なされます
SetParsePath(True、OnePath、False、Mappath);
}
壊す;
}
}
戻りマッパス。
}
/**
*分解された場所と名前に従って、要素名を解析するように設定します
* @param {boolean} end [それは最後ですか]
* @param {string} ele [要素名]
* @param {boolean} propertyValue [プロパティを取るかどうか]
* @param {object} mappath [解析する必要がある要素の名前と、要素に対応する解析モデルの表を設定]
*/
function setParsePath(End、ELE、PropertyValue、Mappath){
var pm = new parsermodel();
pm.setend(end);
//シンボル「$」が値ではない場合
pm.setsinglevalue(!(ele.indexof(dollar)> = 0));
PM.SetPropertyValue(PropertyValue);
//「$」を削除します
ELE = ELE.Replace(Dollar、 '');
MAPPATH [ELE] = PM;
listele.push(ele);
}
// Start the second step --------------------------------------------------------------------------------------------------------------------------
/**
*分解された要素名を、対応する分析モデルに従って、対応するインタープリターオブジェクトに変換します。
* @param {object} mappath [解析された要素名を解析すると、要素に対応する解析モデル]
* @return {array} [各要素を対応するインタープリターオブジェクトの配列に変換]
*/
function mappath2interpreter(mappath){
var list = [];
var pm、key;
var obj = null;
//分解の順にインタープリターオブジェクトに変換する必要があります
for(var i = 0、len = listele.length; i <len; i ++){
key = listele [i];
PM = Mappath [key];
//最後ではありません
if(!pm.isend()){
if(pm.issinglevalue())
//値、変換です
obj = new ElementExpression(key);
それ以外
//複数の値、変換です
obj = new Elementsexpression(key);
} それ以外 {
//最後のものです
//属性値です
if(pm.ispropertyvalue()){
if(pm.issinglevalue())
obj = new Property TerminalExpression(key);
それ以外
obj = new propertySterminalExpression(key);
//要素の値を取得します
} それ以外 {
if(pm.issinglevalue())
obj = new Element TerminalExpression(key);
それ以外
obj = new ElementsterminalExpression(key);
}
}
list.push(obj);
}
返品リスト。
}
// Start the third step --------------------------------------------------------------------------------------------------------------------------
/**
*抽象的な構文ツリーを構築します
* @param {[type]} list [各要素を対応する通訳オブジェクトの配列に変換]
* @return {[type]} [説明]
*/
function buildtree(list){
//最初のオブジェクト、また返されたオブジェクトは、抽象的構文ツリーのルートです
var returnedxmlexpr = null;
//前のオブジェクトを定義します
var prereadxmlexpr = null;
var readxml、ele、eles;
for(var i = 0、len = list.length; i <len; i ++){
readxml = list [i];
//説明は最初の要素です
if(prereadxmlexpr === null){
prereadxmlexpr = readxml;
returnedxmlexpr = readxml;
//前のオブジェクトに要素を追加し、オブジェクトをOldreに設定します
//次のオブジェクトの親ノードとして
} それ以外 {
if(prereadxmlexpr emertaincpression){
ELE = PREREADXMLEXPR;
ELE.ADDELE(readxml);
prereadxmlexpr = readxml;
} else if(prereadxmlexpr ementsexpression){
ELES = PREREADXMLEXPR;
ELES.ADDELE(readxml);
prereadxmlexpr = readxml;
}
}
}
returnedxmlexpr;
}
戻る {
//パブリックメソッド
parse:function(expr){
listele = [];
var mappath = parsemappath(expr);
var list = mappath2interpreter(mappath);
return buildtree(list);
}
};
}();
void function(){
//コンテキストを準備します
var c = new Context( 'interpreter.xml');
//解析して抽象的構文ツリーを取得します
var readxmlexpr = parser.parse( 'root/a/b/d $ .id $');
//解析を要求し、返品値を取得します
var ss = readxmlexpr.interpret(c);
console.log( '------------解析------------');
for(var i = 0、len = ss.length; i <len; i ++){
console.log( 'dプロパティID値は=' + ss [i]);
}
console.log( '--------------解析------------');
//同じコンテキストを使用して継続的に解析する場合は、コンテキストオブジェクトを再現する必要があります
c.reinit();
var readxmlexpr2 = parser.parse( 'root/a/b/d $');
var ss2 = readxmlexpr2.interpret(c);
console.log('------------parsing--------------');
for (i = 0, len = ss2.length; i < len; i++) {
console.log('d的值是= ' + ss2[i]);
}
console.log('---------------parsed--------------');
c.reInit();
var readxmlExpr3 = Parser.parse('root/a/b/c');
var ss3 = readxmlExpr3.interpret(c);
console.log('------------parsing--------------');
console.log('c的name属性值是= ' + ss3);
console.log('---------------parsed--------------');
c.reInit();
var readxmlExpr4 = Parser.parse('root/a/b/c.name');
var ss4 = readxmlExpr4.interpret(c);
console.log('------------parseing--------------');
console.log('c的name属性值是= ' + ss4);
console.log('---------------parsed--------------');
}();
// 这样就实现了类似XPath的部分功能
// 没错,就类似于jQuery选择器的部分功能
}());
输出: d的值是= d1
d的值是= d2
d的值是= d3
d的值是= d4
d的属性id的值是= 1
d的属性id的值是= 2
d的属性id的值是= 3
d的属性id的值是= 4
------------parsing--------------
d的属性id的值是= 1
d的属性id的值是= 2
d的属性id的值是= 3
d的属性id的值是= 4
---------------parsed--------------
------------parsing--------------
d的值是= d1
d的值是= d2
d的值是= d3
d的值是= d4
---------------parsed--------------
------------parsing--------------
c的name属性值是= 12345
---------------parsed--------------
------------parseing--------------
c的name属性值是= testC
---------------parsed--------------