Apakah Shenma adalah "mode juru bahasa"?
Mari kita buka "GoF" terlebih dahulu dan lihat definisi:
Diberi bahasa, tentukan representasi tata bahasanya, dan tentukan seorang penerjemah yang menggunakan representasi ini untuk menafsirkan kalimat dalam bahasa.
Sebelum awal, saya masih perlu mempopulerkan beberapa konsep:
Pohon Sintaks Abstrak:
Pola penerjemah tidak menjelaskan cara membuat pohon sintaks abstrak. Itu tidak melibatkan analisis tata bahasa. Pohon sintaks abstrak dapat diselesaikan dengan program analisis sintaks yang digerakkan oleh tabel, atau dibuat oleh program analisis sintaks tulisan tangan (biasanya rekursif), atau disediakan secara langsung oleh klien.
Parser:
Ini mengacu pada program yang menjelaskan ekspresi yang dibutuhkan oleh panggilan klien dan membentuk pohon sintaksis abstrak setelah penguraian.
Penerjemah:
Mengacu pada program yang menjelaskan pohon sintaks abstrak dan menjalankan fungsi yang sesuai dari setiap node.
Prasyarat penting untuk menggunakan pola interpreter adalah untuk menentukan seperangkat aturan tata bahasa, juga dikenal sebagai tata bahasa. Terlepas dari apakah aturan tata bahasa ini sederhana atau kompleks, aturan ini harus dimasukkan, karena mode penerjemah adalah untuk menganalisis dan melakukan fungsi yang sesuai.
Mari kita lihat diagram struktur dan deskripsi mode penerjemah:
AbstractExpression: Menentukan antarmuka penerjemah dan menyetujui operasi interpretasi penerjemah.
TerminalExpression: terminalExpression, digunakan untuk mengimplementasikan operasi yang terkait dengan Terminator dalam aturan sintaks, tidak lagi berisi penerjemah lain. Jika pola kombinasi digunakan untuk membangun pohon sintaksis abstrak, itu setara dengan objek daun dalam pola kombinasi, dan mungkin ada beberapa penerjemah terminator.
Nonterteralexpression: Interpreter non-terminal, yang digunakan untuk mengimplementasikan operasi yang tidak terkait dengan aturan sintaksis. Biasanya, seorang penerjemah sesuai dengan aturan sintaksis dan dapat berisi penerjemah lainnya. Jika pola komposisi digunakan untuk membangun pohon sintaks abstrak, itu setara dengan objek kombinasi dalam pola komposisi. Mungkin ada beberapa penerjemah non-terminal.
Konteks: Konteks, biasanya berisi data yang diperlukan oleh setiap penerjemah atau fungsi publik.
Klien: Klien mengacu pada klien yang menggunakan juru bahasa. Biasanya, ekspresi yang dibuat sesuai dengan sintaksis bahasa dikonversi menjadi pohon sintaks abstrak yang dijelaskan oleh objek interpreter, dan kemudian operasi penjelasan disebut.
Di sini kita menggunakan contoh XML untuk memahami pola interpreter:
Pertama, kita perlu merancang tata bahasa sederhana untuk ekspresi. Untuk tujuan umum, gunakan root untuk mewakili elemen root, ABC, dll. Untuk mewakili elemen. XML sederhana adalah sebagai berikut:
Salinan kode adalah sebagai berikut:
<? Xml Version = "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>
</soot>
Tata bahasa ekspresi konvensi adalah sebagai berikut:
1. Dapatkan nilai elemen tunggal: Mulai dari elemen root dan sampai ke elemen yang Anda inginkan untuk mendapatkan nilainya. Bagian tengah elemen dipisahkan oleh "/" dan tidak "/" ditambahkan sebelum elemen root. Misalnya, ekspresi "root/a/b/c" berarti mendapatkan nilai elemen C di bawah elemen root, elemen A, elemen B, dan elemen c.
2. Dapatkan nilai atribut dari satu elemen: tentu saja ada beberapa atribut. Atribut untuk mendapatkan nilai harus menjadi atribut elemen terakhir dari ekspresi. Menambahkan "." Setelah elemen terakhir dan kemudian tambahkan nama atribut. Misalnya, ekspresi "root/a/b/c.name" berarti mendapatkan nilai atribut nama elemen root, elemen A, elemen B, elemen c.
3. Dapatkan nilai nama elemen yang sama, tentu saja, ada beberapa elemen. Elemen untuk mendapatkan nilai harus menjadi elemen terakhir dari ekspresi, dan menambahkan "$" setelah elemen terakhir. Misalnya, ekspresi "root/a/b/d $" mewakili kumpulan nilai beberapa elemen D di bawah elemen root, di bawah elemen A, dan di bawah elemen B.
4. Dapatkan nilai atribut dengan nama elemen yang sama, tentu saja, ada beberapa: elemen untuk mendapatkan nilai atribut harus menjadi elemen terakhir dari ekspresi, dan menambahkan "$" setelah elemen terakhir. Misalnya, ekspresi "root/a/b/d $ .id $" mewakili kumpulan nilai beberapa atribut ID elemen D di bawah elemen root, di bawah elemen A, dan di bawah elemen B.
XML di atas, sesuai dengan pohon sintaks abstrak, dan struktur yang mungkin ditunjukkan pada gambar:
Mari kita lihat kode spesifik di bawah ini:
1. Tentukan konteksnya:
Salinan kode adalah sebagai berikut:
/**
* Konteks, digunakan untuk berisi beberapa informasi global yang diperlukan oleh penerjemah
* @param {string} filepathname [path dan nama XML yang perlu dibaca]
*/
konteks fungsi (filepathname) {
// elemen olahan sebelumnya
this.preele = null;
// objek dokumen XML
this.document = xmlutil.getroot (filepathname);
}
Context.prototype = {
// Menghidupkan kembali konteksnya
reinit: function () {
this.preele = null;
},
/**
* Metode untuk penggunaan publik setiap ekspresi
* Dapatkan elemen saat ini sesuai dengan nama elemen induk dan elemen saat ini
* @param {elemen} pele [elemen induk]
* @param {string} elename [nama elemen saat ini]
* @return {elemen | null} [elemen saat ini ditemukan]
*/
getNowele: function (pele, elename) {
var tempnodelist = pele.childnodes;
var nowele;
untuk (var i = 0, len = tempnodelist.length; i <len; i ++) {
if ((nowele = tempnodelist [i]). nodetype === 1)
if (nowele.nodename === elename)
Kembalikan Nowele;
}
kembali nol;
},
getPreele: function () {
kembalikan ini.pele;
},
setPreele: function (preele) {
this.preele = preele;
},
getDocument: function () {
mengembalikan ini. Dokumen;
}
};
Dalam konteksnya, saya menggunakan objek alat xmlutil untuk mendapatkan xmldom. Di bawah ini saya menggunakan DomPaser dari DOM3. Beberapa browser mungkin tidak mendukungnya. Harap gunakan browser dasar:
Salinan kode adalah sebagai berikut:
// Objek alat
// Parse XML untuk mendapatkan objek dokumen yang sesuai
var xmlutil = {
getroot: function (filePathName) {
var parser = domparser baru ();
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> id = "4"> d4 </d> </b> </a> </soot> ',' text/xml ');
mengembalikan xmldom;
}
};
Ini kode untuk penerjemah:
Salinan kode adalah sebagai berikut:
/**
* Elemen digunakan sebagai penerjemah yang sesuai dengan non-terminal untuk menafsirkan dan menjalankan elemen perantara
* @param {string} elename [nama elemen]
*/
fungsi elementExpression (elename) {
this.eles = [];
this.elename = elename;
}
ElementExpression.prototype = {
addele: function (elename) {
this.eles.push (elename);
Kembali Benar;
},
lepas: function (ele) {
untuk (var i = 0, len = this.eles.length; i <len; i ++) {
if (ele === this.eles [i])
this.eles.splice (i--, 1);
}
Kembali Benar;
},
interpret: function (context) {
// Pertama ambil elemen saat ini dalam konteks sebagai elemen induk
// Temukan elemen XML yang sesuai dengan nama elemen saat ini dan atur kembali ke konteks
var pele = context.getPele ();
if (! pele) {
// menunjukkan bahwa elemen root sekarang diperoleh
context.setPele (context.getDocument (). DocumentElement);
} kalau tidak {
// Dapatkan elemen saat ini berdasarkan nama elemen induk dan elemen yang akan dicari
var nowele = context.getnowele (pele, this.elename);
// Masukkan elemen yang saat ini diambil ke dalam konteks
Context.setPele (Nowele);
}
var ss;
// Loop untuk memanggil metode interpretasi dari elemen anak
untuk (var i = 0, len = this.eles.length; i <len; i ++) {
ss = this.eles [i] .interpret (konteks);
}
// Kembalikan hasil penjelasan dari penerjemah terakhir. Secara umum, penerjemah terakhir adalah penerjemah Terminator.
mengembalikan ss;
}
};
/**
* Elemen digunakan sebagai penerjemah yang sesuai dengan Terminator
* @param {string} nama [nama elemen]
*/
fungsi elementterminalexpression (name) {
this.elename = name;
}
Elementterminalexpression.prototype = {
interpret: function (context) {
var pele = context.getPele ();
var ele = null;
if (! pele) {
ele = context.getDocument (). DocumentElement;
} kalau tidak {
ele = context.getnowele (pele, this.elename);
Context.setPele (ELE);
}
// Dapatkan nilai elemen
return ELE.FirstChild.NODEVALUE;
}
};
/**
* Atribut digunakan sebagai penerjemah yang sesuai dengan Terminator
* @param {string} propname [nama atribut]
*/
fungsi propertyterminexpression (propName) {
this.propname = propName;
}
PropertIterSexpression.prototype = {
interpret: function (context) {
// Dapatkan nilai atribut elemen terakhir secara langsung
return context.getPele (). getAttribute (this.propname);
}
};
Pertama -tama mari kita lihat cara menggunakan penerjemah untuk mendapatkan nilai satu elemen:
Salinan kode adalah sebagai berikut:
function void () {
var c = konteks baru ();
// Ingin mendapatkan nilai beberapa elemen D, yaitu nilai dari ekspresi berikut: "root/a/b/c"
// Pertama, Anda perlu membangun pohon sintaksis abstrak untuk penerjemah
var root = ElementExpression baru ('root');
var aele = element expression baru ('a');
var bele = new Expression ('b');
var cele = elementterminalexpression baru ('c');
// kombinasi
root.addele (aele);
Aele.addele (Bele);
Bele.addele (Cele);
Console.log (nilai C adalah = ' + root.interpret (c));
} ();
Output: Nilai C adalah = 12345
Kemudian kami menggunakan kode di atas untuk mendapatkan nilai atribut dari satu elemen:
Salinan kode adalah sebagai berikut:
function void () {
var c = konteks baru ();
// Ingin mendapatkan atribut ID dari elemen D, yaitu nilai dari ekspresi berikut: "A/B/C.Name"
// kali ini c belum berakhir, dan Anda perlu memodifikasi C menjadi elemen ekspresi
var root = ElementExpression baru ('root');
var aele = element expression baru ('a');
var bele = new Expression ('b');
var cele = element expression baru ('c');
var prop = PropertyterMinExpression ('name') baru;
// kombinasi
root.addele (aele);
Aele.addele (Bele);
Bele.addele (Cele);
Cele.addele (prop);
Console.log (nilai nama properti C adalah = ' + root.interpret (c));
// Jika Anda ingin menggunakan konteks yang sama dan parse terus menerus, Anda perlu menginisialisasi kembali objek konteks
// Misalnya, Anda perlu mendapatkan kembali nilai nama atribut lagi secara berurutan, tentu saja Anda dapat menggabungkan kembali elemen-elemen tersebut
// PSE-PARSE, selama konteks yang sama digunakan, objek konteks perlu diinisialisasi ulang
c.reinit ();
Console.log ('Nilai Nama Properti Reget C adalah =' + root.interpret (c));
} ();
Output: Nilai Nama Atribut C IS = Testc Ambil Nilai Nama Atribut C IS = Testc
menjelaskan:
1. Fungsi Mode Interpreter:
Pola interpreter menggunakan objek interpreter untuk mewakili dan memproses aturan sintaks yang sesuai. Secara umum, seorang penerjemah menangani aturan sintaksis. Secara teoritis, selama ekspresi yang sesuai sintaks dapat diwakili oleh objek interpreter dan dapat membentuk pohon sintaks abstrak, pola interpreter dapat digunakan untuk menanganinya.
2. Aturan dan Penerjemah Sintaksis
Ada korespondensi antara aturan sintaks dan penerjemah. Secara umum, seorang penerjemah menangani aturan sintaks, tetapi yang sebaliknya tidak benar. Aturan sintaks dapat memiliki beberapa interpretasi dan pemrosesan, yaitu, aturan sintaks dapat sesuai dengan beberapa penerjemah.
3. Konteks Kesamaan
Konteks memainkan peran yang sangat penting dalam mode interpreter. Karena konteksnya diteruskan ke semua penerjemah. Oleh karena itu, keadaan penerjemah dapat disimpan dan diakses dalam konteks. Misalnya, juru bahasa sebelumnya dapat menyimpan beberapa data dalam konteks, dan penerjemah yang terakhir dapat memperoleh nilai -nilai ini.
Selain itu, beberapa data di luar penerjemah dapat dilewati melalui konteks, tetapi penerjemah membutuhkannya, dan beberapa data publik global.
Ada juga fungsi dalam konteks, yaitu dapat memberikan fungsi umum dari semua objek interpreter, mirip dengan kombinasi objek, daripada menggunakan pewarisan untuk mendapatkan fungsi umum, yang dapat dipanggil dalam setiap objek interpreter.
4. Siapa yang akan membangun pohon sintaksis abstrak
Dalam contoh sebelumnya, sangat merepotkan untuk secara manual membangun pohon sintaks abstrak di sisi klien, tetapi dalam mode interpreter, bagian fungsi ini tidak terlibat, dan hanya bertanggung jawab untuk menafsirkan dan memproses pohon sintaks abstrak yang dibangun. Kami akan memperkenalkan bahwa kami dapat memberikan parser untuk mengubah ekspresi menjadi pohon sintaksis abstrak.
Ada masalah lain, yaitu, aturan sintaks dapat sesuai dengan beberapa objek interpreter, yaitu, elemen yang sama dapat dikonversi menjadi beberapa objek interpreter, yang berarti bahwa ekspresi yang sama dapat membentuk pohon sintaks abstrak yang tidak perlu, yang juga membuat sulit untuk membangun pohon sintaks abstrak dan beban kerja sangat besar.
5. Siapa yang bertanggung jawab untuk menjelaskan operasi
Selama pohon sintaks abstrak didefinisikan, penerjemah harus bertanggung jawab untuk menafsirkan dan mengeksekusi. Meskipun ada aturan sintaks yang berbeda, juru bahasa tidak bertanggung jawab untuk memilih objek interpreter mana yang akan digunakan untuk menafsirkan aturan sintaks eksekusi. Fungsi pemilihan penerjemah selesai saat membangun pohon sintaksis abstrak.
6. Urutan Call of Interpreter Mode
1) Buat Objek Konteks
2) Buat beberapa objek interpreter dan gabungkan pohon sintaks abstrak
3) Memanggil operasi interpretasi objek interpreter
3.1) menyimpan dan mengakses keadaan penerjemah melalui konteks.
Untuk objek interpreter non-terminator, secara rekursif panggil objek subinterpreter yang dikandungnya.
Inti dari pola interpreter: *implementasi terpisah, menafsirkan eksekusi *
Modul interpreter menggunakan objek interpreter untuk memproses aturan sintaksis untuk memisahkan fungsi yang kompleks; kemudian memilih fungsi yang perlu dieksekusi, dan menggabungkan fungsi -fungsi ini menjadi pohon sintaks abstrak yang perlu ditafsirkan dan dieksekusi; kemudian menginterpretasikan eksekusi sesuai dengan pohon sintaksis abstrak untuk mengimplementasikan fungsi yang sesuai.
Di permukaan, mode interpreter berfokus pada pemrosesan sintaks khusus yang biasanya tidak kita gunakan; Tetapi pada dasarnya, gagasan mode interpreter adalah pemisahan, enkapsulasi, penyederhanaan, dan sama dengan banyak mode.
Misalnya, mode interpreter dapat digunakan untuk mensimulasikan fungsi mode status. Jika sintaks yang akan diproses oleh mode penerjemah disederhanakan hanya satu tanda keadaan, penerjemah dianggap sebagai objek pemrosesan untuk negara. Untuk sintaks yang sama yang mewakili negara bagian, mungkin ada banyak penerjemah yang tidak digunakan, yaitu, ada banyak objek dengan keadaan pemrosesan yang berbeda. Saat membuat pohon sintaks abstrak, disederhanakan untuk membuat penerjemah yang sesuai berdasarkan pada tanda negara, dan tidak perlu membangun pohon.
Demikian pula, mode penerjemah dapat mensimulasikan fungsi menerapkan mode kebijakan, fungsi mode dekorator, dll., Terutama proses mensimulasikan fungsi mode dekorator, dan proses membangun pohon sintaks abstrak secara alami akan sesuai dengan proses menggabungkan dekorator.
Mode interpreter biasanya tidak cepat (kebanyakan sangat lambat), dan kesalahan debugging sulit (Bagian 1: Meskipun debugging sulit, itu sebenarnya mengurangi kemungkinan kesalahan), tetapi keunggulannya jelas. Ini dapat secara efektif mengontrol kompleksitas antarmuka antar modul. Untuk fungsi yang memiliki frekuensi eksekusi rendah tetapi frekuensi kode tinggi yang cukup, dan sangat beragam, penerjemah sangat cocok untuk mode. Selain itu, penerjemah memiliki keuntungan lain yang kurang nyata, yaitu bahwa ia dapat dengan mudah lintas-bahasa dan lintas platform.
Keuntungan dan Kekurangan Mode Interpreter:
keuntungan:
1. Sintaks yang mudah diimplementasikan
Dalam mode interpreter, aturan sintaks ditafsirkan dengan objek interpreter untuk menafsirkan eksekusi. Untuk implementasi penerjemah, fungsi menjadi relatif sederhana. Anda hanya perlu mempertimbangkan implementasi aturan sintaks ini, dan Anda tidak perlu khawatir tentang hal lain. 2. Mudah memperluas sintaks baru
Justru karena cara objek interpreter bertanggung jawab atas aturan sintaks yang memperluas sintaks baru sangat mudah. Sintaks baru telah diperluas, dan Anda hanya perlu membuat objek interpreter yang sesuai dan menggunakan objek interpreter baru ini saat membuat pohon sintaks abstrak.
kekurangan:
Tidak cocok untuk sintaks yang kompleks
Jika sintaksnya sangat kompleks, karya membangun pohon sintaks abstrak yang dibutuhkan oleh pola penerjemah sangat sulit, dan dimungkinkan untuk membangun beberapa pohon sintaks abstrak. Oleh karena itu, pola interpreter tidak cocok untuk sintaks yang kompleks. Mungkin lebih baik menggunakan penganalisa sintaks atau generator kompiler.
Kapan menggunakannya?
Ketika ada bahasa yang perlu ditafsirkan dan dieksekusi, dan kalimat dalam bahasa itu dapat direpresentasikan sebagai pohon sintaks abstrak, Anda dapat mempertimbangkan untuk menggunakan pola penerjemah.
Saat menggunakan mode interpreter, ada dua fitur lain yang perlu dipertimbangkan. Salah satunya adalah bahwa sintaks harus relatif sederhana. Sintaks yang terlalu bertanggung jawab tidak cocok untuk menggunakan mode interpreter. Yang lainnya adalah bahwa persyaratan efisiensi tidak terlalu tinggi dan persyaratan efisiensinya sangat tinggi, dan tidak cocok untuk digunakan.
Pendahuluan sebelumnya adalah bagaimana mendapatkan nilai elemen tunggal dan nilai atribut elemen tunggal. Mari kita lihat bagaimana mendapatkan nilai -nilai beberapa elemen, serta nilai -nilai nama satu sama lain dalam beberapa elemen, dan tes sebelumnya semuanya merupakan pohon sintaks abstrak yang digabungkan secara artifisial. Kami juga mengimplementasikan parser sederhana berikut untuk mengonversi ekspresi yang sesuai dengan sintaks yang ditentukan di atas menjadi pohon sintaks abstrak dari penerjemah yang diimplementasikan di atas: Saya langsung memposting kode:
Salinan kode adalah sebagai berikut:
// Baca nilai beberapa elemen atau atribut
(fungsi () {
/**
* Konteks, digunakan untuk berisi beberapa informasi global yang diperlukan oleh penerjemah
* @param {string} filepathname [path dan nama XML yang perlu dibaca]
*/
konteks fungsi (filepathname) {
// Beberapa elemen yang diproses di sebelumnya
this.peeles = [];
// objek dokumen XML
this.document = xmlutil.getroot (filepathname);
}
Context.prototype = {
// Menghidupkan kembali konteksnya
reinit: function () {
this.peeles = [];
},
/**
* Metode untuk penggunaan publik setiap ekspresi
* Dapatkan elemen saat ini sesuai dengan nama elemen induk dan elemen saat ini
* @param {elemen} pele [elemen induk]
* @param {string} elename [nama elemen saat ini]
* @return {elemen | null} [elemen saat ini ditemukan]
*/
getnoweles: function (pele, elename) {
var elemen = [];
var tempnodelist = pele.childnodes;
var nowele;
untuk (var i = 0, len = tempnodelist.length; i <len; i ++) {
if ((nowele = tempnodelist [i]). nodetype === 1) {
if (nowele.nodename === elename) {
elemen.push (nowele);
}
}
}
elemen pengembalian;
},
getPreeles: function () {
kembalikan ini. Preeles;
},
setPreeles: function (noweles) {
this.peeles = noweles;
},
getDocument: function () {
mengembalikan ini. Dokumen;
}
};
// Objek alat
// Parse XML untuk mendapatkan objek dokumen yang sesuai
var xmlutil = {
getroot: function (filePathName) {
var parser = domparser baru ();
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> id = "4"> d4 </d> </b> </a> </soot> ',' text/xml ');
mengembalikan xmldom;
}
};
/**
* Elemen digunakan sebagai penerjemah yang sesuai dengan non-terminal untuk menafsirkan dan menjalankan elemen perantara
* @param {string} elename [nama elemen]
*/
fungsi elementExpression (elename) {
this.eles = [];
this.elename = elename;
}
ElementExpression.prototype = {
addele: function (elename) {
this.eles.push (elename);
Kembali Benar;
},
lepas: function (ele) {
untuk (var i = 0, len = this.eles.length; i <len; i ++) {
if (ele === this.eles [i]) {
this.eles.splice (i--, 1);
}
}
Kembali Benar;
},
interpret: function (context) {
// Pertama ambil elemen saat ini dalam konteks sebagai elemen induk
// Temukan elemen XML yang sesuai dengan nama elemen saat ini dan atur kembali ke konteks
var peles = context.getPeles ();
var ele = null;
var noweles = [];
if (! peles.length) {
// menunjukkan bahwa elemen root sekarang diperoleh
ele = context.getDocument (). DocumentElement;
peles.push (ele);
Context.setPeles (peles);
} kalau tidak {
var Tempele;
untuk (var i = 0, len = peles.length; i <len; i ++) {
tempele = peles [i];
noweles = noweles.concat (context.getnoweles (tempele, this.elename));
// Berhenti jika Anda menemukannya
if (noweles.length) break;
}
Context.setPeles ([Noweles [0]]);
}
var ss;
// Loop untuk memanggil metode interpretasi dari elemen anak
untuk (var i = 0, len = this.eles.length; i <len; i ++) {
ss = this.eles [i] .interpret (konteks);
}
mengembalikan ss;
}
};
/**
* Elemen digunakan sebagai penerjemah yang sesuai dengan Terminator
* @param {string} nama [nama elemen]
*/
fungsi elementterminalexpression (name) {
this.elename = name;
}
Elementterminalexpression.prototype = {
interpret: function (context) {
var peles = context.getPeles ();
var ele = null;
if (! peles.length) {
ele = context.getDocument (). DocumentElement;
} kalau tidak {
ele = context.getnoweles (peles [0], this.elename) [0];
}
// Dapatkan nilai elemen
return ELE.FirstChild.NODEVALUE;
}
};
/**
* Atribut digunakan sebagai penerjemah yang sesuai dengan Terminator
* @param {string} propname [nama atribut]
*/
fungsi propertyterminexpression (propName) {
this.propname = propName;
}
PropertIterSexpression.prototype = {
interpret: function (context) {
// Dapatkan nilai atribut elemen terakhir secara langsung
return context.getPeles () [0] .getAttribute (this.propname);
}
};
/**
* Banyak atribut digunakan sebagai penerjemah yang sesuai dengan terminator
* @param {string} propname [nama atribut]
*/
function propertysterminalExpression (propName) {
this.propname = propName;
}
PropertiySterSterMinExpression.prototype = {
interpret: function (context) {
var eles = context.getPeles ();
var ss = [];
untuk (var i = 0, len = eles.length; i <len; i ++) {
ss.push (eles [i] .getAttribute (this.propname));
}
mengembalikan ss;
}
};
/**
* Objek pemrosesan interpretasi dengan beberapa elemen sebagai terminator
* @param {[type]} nama [deskripsi]
*/
elemen fungsionterexpression (name) {
this.elename = name;
}
Elemementterminalexpression.prototype = {
interpret: function (context) {
var peles = context.getPeles ();
var noweles = [];
untuk (var i = 0, len = peles.length; i <len; i ++) {
noweles = noweles.concat (context.getnoweles (peles [i], this.elename));
}
var ss = [];
untuk (i = 0, len = noweles.length; i <len; i ++) {
ss.push (noweles [i] .firstchild.nodevalue);
}
mengembalikan ss;
}
};
/**
* Beberapa elemen ditafsirkan sebagai non-terminal
*/
function elementsExpression (name) {
this.elename = name;
this.eles = [];
}
ElementsExpression.prototype = {
interpret: function (context) {
var peles = context.getPeles ();
var noweles = [];
untuk (var i = 0, len = peles.length; i <len; i ++) {
noweles = noweles.concat (context.getnoweles (peles [i], this.elename));
}
Context.setPeles (Noweles);
var ss;
untuk (i = 0, len = this.eles.length; i <len; i ++) {
ss = this.eles [i] .interpret (konteks);
}
mengembalikan ss;
},
addele: function (ele) {
this.eles.push (ele);
Kembali Benar;
},
lepas: function (ele) {
untuk (var i = 0, len = this.eles.length; i <len; i ++) {
if (ele === this.eles [i]) {
this.eles.splice (i--, 1);
}
}
Kembali Benar;
}
};
function void () {
// "root/a/b/d $"
var c = konteks baru ('interpreter.xml');
var root = ElementExpression baru ('root');
var aele = element expression baru ('a');
var bele = new Expression ('b');
var dele = elemen baru terminalExpression ('d');
root.addele (aele);
Aele.addele (Bele);
bele.addele (dele);
var ss = root.interpret (c);
untuk (var i = 0, len = ss.length; i <len; i ++) {
console.log (nilai d adalah = ' + ss [i]);
}
} ();
function void () {
// a/b/d $ .id $
var c = konteks baru ('interpreter.xml');
var root = ElementExpression baru ('root');
var aele = element expression baru ('a');
var bele = new Expression ('b');
var dele = elementsExpression baru ('d');
var prop = properti baru sterminalExpression ('id');
root.addele (aele);
Aele.addele (Bele);
bele.addele (dele);
dele.addele (prop);
var ss = root.interpret (c);
untuk (var i = 0, len = ss.length; i <len; i ++) {
Console.log ('D Nilai id properti adalah =' + ss [i]);
}
} ();
// parser
/**
* Ide Implementasi Parser
* 1. Mengurai ekspresi yang disahkan oleh klien, menguraikannya menjadi elemen satu per satu, dan menggunakan model analitik yang sesuai untuk merangkum beberapa informasi tentang elemen ini.
* 2. Konversikan menjadi objek parser yang sesuai berdasarkan informasi dari setiap elemen.
* 3. Gabungkan objek parser ini untuk mendapatkan pohon sintaks abstrak.
*
* Mengapa Anda tidak menggabungkan 1 dan 2, dan secara langsung menguraikan suatu elemen dan mengubahnya menjadi objek parser yang sesuai?
* 1. Pemisahan fungsional, jangan membuat fungsi metode terlalu rumit.
* 2. Untuk modifikasi dan ekstensi di masa depan, sintaksnya sekarang sederhana, jadi ada sedikit yang perlu dipertimbangkan ketika mengubah menjadi objek parser, dan tidak sulit untuk mengonversi secara langsung, tetapi jika sintaksnya rumit, konversi langsung akan sangat berantakan.
*/
/**
* Digunakan untuk merangkum atribut yang sesuai dari masing -masing elemen yang diuraikan
*/
fungsi parsermodel () {
// apakah satu nilai
this.singlevalue;
// Apakah itu atribut, baik atribut atau elemen
this.propertyvalue;
// apakah akan berakhir
this.end;
}
Parsermodel.prototype = {
isEnd: function () {
kembalikan ini.end;
},
setend: function (end) {
this.end = end;
},
issinglevalue: function () {
kembalikan ini.singlevalue;
},
setsinglevalue: function (onevalue) {
this.singlevalue = OneValue;
},
isPropertyValue: function () {
kembalikan ini.propertyValue;
},
setPropertyValue: function (PropertyValue) {
this.propertyValue = PropertyValue;
}
};
var parser = function () {
var backlash = '/';
var dot = '.';
var dollar = '$';
// Catat nama elemen yang perlu diuraikan sesuai dengan urutan dekomposisi
var listele = null;
// Mulai langkah pertama ----------------------------------------------------------------------------------------------------------------------
/**
* Lewati ekspresi string, dan kemudian piringkan dan gabungkan menjadi pohon sintaksis abstrak
* @param {string} expr [Jelaskan ekspresi string untuk mengambil nilai]
* @return {objek} [pohon sintaks abstrak yang sesuai]
*/
fungsi parsemappath (expr) {
// Pertama membagi string menurut "/"
var tokenizer = expr.split (backlash);
// tabel nilai terurai
var mappath = {};
var onepath, elename, propname;
var dotindex = -1;
untuk (var i = 0, len = tokenizer.length; i <len; i ++) {
OnePath = tokenizer [i];
if (tokenizer [i + 1]) {
// Ada nilai lain, yang berarti bahwa ini bukan elemen terakhir
// Menurut sintaks saat ini, atribut harus di akhir, jadi itu bukan atribut.
setParsepath (false, onePath, false, mappath);
} kalau tidak {
// ini akhirnya
dotIndex = onePath.indexof (dot);
if (dotIndex> = 0) {
// itu berarti Anda ingin mendapatkan nilai atribut, jadi bagilah sesuai dengan "."
// Yang pertama adalah nama elemen, dan yang kedua adalah nama atribut
elename = onePath.substring (0, dotindex);
PropName = OnePath.SubString (dotIndex + 1);
// Elemen di depan properti secara alami bukan yang terakhir, juga bukan properti
setParsepath (false, elename, false, mappath);
// Atur atribut. Menurut definisi sintaks saat ini, atribut hanya bisa menjadi yang terakhir.
setParsepath (true, propname, true, mappath);
} kalau tidak {
// Instruksi diambil sebagai nilai elemen, dan nilai elemen terakhir
setParsepath (true, onePath, false, mappath);
}
merusak;
}
}
mengembalikan mappath;
}
/**
* Tetapkan nama elemen yang akan diuraikan sesuai dengan lokasi dan nama yang terurai
* @param {boolean} end [apakah itu terakhir]
* @param {string} ele [nama elemen]
* @param {boolean} propertivalue [apakah akan mengambil properti]
* @param {objek} mappath [Tetapkan nama elemen yang perlu diuraikan, dan tabel model parsing yang sesuai dengan elemen]
*/
fungsi setParsePath (end, ele, propertyvalue, mappath) {
var pm = parsermodel baru ();
pm.setend (akhir);
// Jika simbol "$" bukan nilai
pm.setsinglevalue (! (Ele.indexof (dolar)> = 0));
PM.setPropertyValue (PropertyValue);
// hapus "$"
ele = ele.replace (dolar, '');
mappath [ele] = pm;
listele.push (ele);
}
// Mulai langkah kedua --------------------------------------------------------------------------------------------------------------------------
/**
* Konversi nama elemen yang didekomposisi menjadi objek interpreter yang sesuai sesuai dengan model analitik yang sesuai.
* @param {objek} mappath [Nama elemen yang didekomposisi untuk diuraikan, dan model parsing yang sesuai dengan elemen]
* @return {array} [Konversi setiap elemen menjadi array objek interpreter yang sesuai]
*/
fungsi mappath2intreter (mappath) {
var list = [];
var pm, kunci;
var obj = null;
// perlu untuk mengubahnya menjadi objek interpreter dalam urutan dekomposisi
untuk (var i = 0, len = listele.length; i <len; i ++) {
key = listele [i];
pm = mappath [kunci];
// bukan yang terakhir
if (! pm.isend ()) {
if (pm.issinglevalue ())
// adalah nilai, konversi
OBJ = Expression Element baru (key);
kalau tidak
// adalah beberapa nilai, konversi
obj = elementsExpression baru (key);
} kalau tidak {
// Ini yang terakhir
// adalah nilai atribut
if (pm.ispropertyValue ()) {
if (pm.issinglevalue ())
obj = properti baru terminalExpression (kunci);
kalau tidak
OBJ = Properti Baru Baru. Expression (Key);
// ambil nilai elemen
} kalau tidak {
if (pm.issinglevalue ())
OBJ = ElementterminalExpression baru (kunci);
kalau tidak
obj = elemen baru terminalExpression (kunci);
}
}
list.push (obj);
}
daftar pengembalian;
}
// Mulai langkah ketiga ------------------------------------------------------------------------------------------------------------------------------
/**
* Bangun pohon sintaksis abstrak
* @param {[type]} Daftar [Konversi setiap elemen menjadi array objek interpreter yang sesuai]
* @return {[type]} [deskripsi]
*/
fungsi buildtree (daftar) {
// Objek pertama, juga objek yang dikembalikan, adalah akar dari pohon sintaks abstrak
var returnReadxMlexpr = null;
// Tentukan objek sebelumnya
var prereadxmlexpr = null;
var readxml, ele, eles;
untuk (var i = 0, len = list.length; i <len; i ++) {
readxml = daftar [i];
// Deskripsi adalah elemen pertama
if (prereadxmlexpr === null) {
prereadxmlexpr = readxml;
returnReadxMlexpr = readxml;
// Tambahkan elemen ke objek sebelumnya dan atur objek ke oldre
// sebagai simpul induk dari objek berikutnya
} kalau tidak {
if (prereadxmlexpr instance dari elementExpression) {
ele = preadxmlexpr;
ele.addele (readxml);
prereadxmlexpr = readxml;
} lain jika (preadxmlexpr instanceof elementsExpression) {
eles = prereadxmlexpr;
eles.addele (readxml);
prereadxmlexpr = readxml;
}
}
}
return returnereadxmlexpr;
}
kembali {
// Metode publik
Parse: function (expr) {
listele = [];
var mappath = parsemappath (expr);
var list = mappath2intreter (mappath);
return buildtree (daftar);
}
};
} ();
function void () {
// Siapkan konteksnya
var c = konteks baru ('interpreter.xml');
// Dapatkan pohon sintaks abstrak dengan parsing
var readxmlexpr = parser.parse ('root/a/b/d $ .id $');
// Minta parsing dan dapatkan nilai pengembalian
var ss = readxmlexpr.interpret (c);
console.log ('------------ parsing --------------');
untuk (var i = 0, len = ss.length; i <len; i ++) {
Console.log ('D Nilai id properti adalah =' + ss [i]);
}
Console.log ('--------------- Parsed --------------');
// Jika Anda ingin menggunakan konteks yang sama dan parse terus menerus, Anda perlu menginisialisasi kembali objek konteks
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--------------