هل شينما "وضع مترجم"؟
دعنا نفتح "GOF" أولاً ونلقي نظرة على التعريف:
بالنظر إلى لغة ، حدد تمثيل قواعدها ، وتحديد مترجم يستخدم هذا التمثيل لتفسير الجمل باللغة.
قبل البداية ، ما زلت بحاجة إلى تعميم العديد من المفاهيم:
شجرة بناء الجملة المجردة:
لا يفسر نمط المترجم المترجم كيفية إنشاء شجرة بناء جملة مجردة. لا ينطوي على تحليل نحوي. يمكن إكمال شجرة بناء الجملة المجردة بواسطة برنامج تحليل بناء الجملة الذي يحركه الجدول ، أو تم إنشاؤه بواسطة برنامج تحليل بناء الجملة المكتوب بخط اليد (عادةً ما يكون متكررًا) ، أو يقدمه العميل مباشرة.
المحلل:
يشير إلى برنامج يصف التعبيرات التي تتطلبها مكالمة العميل وتشكل شجرة بناء جملة مجردة بعد التحليل.
مترجم:
يشير إلى برنامج يشرح شجرة بناء الجملة المجردة وتنفيذ الوظائف المقابلة لكل عقدة.
يتمثل أحد المتطلبات المسبقة لاستخدام نمط المترجم المترجم في تحديد مجموعة من القواعد النحوية ، والمعروفة أيضًا باسم القواعد. بغض النظر عما إذا كانت قواعد هذه القواعد بسيطة أو معقدة ، يجب تضمين هذه القواعد ، لأن وضع المترجم هو تحليل وتنفيذ الوظائف المقابلة وفقًا لذلك.
دعنا نلقي نظرة على مخطط الهيكل ووصف وضع المترجم المترجم:
AbstractExpression: يحدد واجهة المترجم المترجم ويوافق على عملية تفسير المترجم.
TerminalExpression: Terminalexpression ، المستخدمة لتنفيذ العمليات المتعلقة بالمقدمة في قواعد بناء الجملة ، لم يعد يحتوي على المترجمين الفوريين الآخرين. إذا تم استخدام نمط التركيبة لإنشاء شجرة بناء جملة مجردة ، فإنه يعادل كائن ورقة في نمط الجمع ، ويمكن أن يكون هناك متعددين من فترات الفوريين المنهي.
عدم الطول: مترجم غير طرفي ، يستخدم لتنفيذ العمليات غير المرتبطة بالمحطة في قواعد بناء الجملة. عادة ، يتوافق المترجم المترجم مع قاعدة بناء الجملة ويمكن أن يحتوي على فترات فوريين آخرين. إذا تم استخدام نمط التكوين لإنشاء شجرة بناء جملة مجردة ، فإنه يعادل كائن مزيج في نمط التكوين. يمكن أن يكون هناك متعددين من المترجمين الفوريين غير الطرفي.
السياق: السياق ، وعادة ما يحتوي على البيانات المطلوبة من قبل كل مترجم أو وظائف عامة.
العميل: يشير العميل إلى عميل يستخدم مترجمًا. عادةً ما يتم تحويل التعبيرات التي تم إجراؤها وفقًا لبناء اللغة إلى شجرة بناء جملة مجردة موصوفة بواسطة كائن المترجم ، ثم تسمى عملية التفسير.
هنا نستخدم مثال XML لفهم نمط المترجم المترجم:
أولاً ، نحتاج إلى تصميم قواعد بسيطة للتعبير. لغرض عام ، استخدم الجذر لتمثيل عنصر الجذر ، ABC ، وما إلى ذلك لتمثيل العنصر. XML بسيط كما يلي:
نسخة الكود كما يلي:
<؟
<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. احصل على قيمة عنصر واحد: ابدأ من عنصر الجذر وبداخل العنصر الذي تريد الحصول على القيمة. يتم فصل منتصف العنصر بـ "/" ولا تتم إضافة "/" قبل عنصر الجذر. على سبيل المثال ، يعني تعبير "الجذر/A/B/C" الحصول على قيم العنصر C تحت عنصر الجذر ، العنصر A ، العنصر B ، والعنصر C.
2. احصل على قيمة سمة عنصر واحد: بالطبع هناك سمات متعددة. يجب أن تكون السمة للحصول على القيمة سمة العنصر الأخير من التعبير. يضيف "." بعد العنصر الأخير ثم أضف اسم السمة. على سبيل المثال ، يعني تعبير "Root/A/B/C.Name" الحصول على قيمة سمة الاسم لعنصر الجذر ، العنصر A ، العنصر B ، العنصر C.
3. احصل على قيمة اسم العنصر نفسه ، بالطبع ، هناك عناصر متعددة. يجب أن يكون عنصر الحصول على القيمة هو العنصر الأخير من التعبير ، وإضافة "$" بعد العنصر الأخير. على سبيل المثال ، يمثل تعبير "Root/A/B/D $" مجموعة قيم عناصر D المتعددة تحت عنصر الجذر ، وتحت العنصر A ، وتحت عنصر B.
4. احصل على قيمة السمة بنفس اسم العنصر ، بالطبع ، هناك عدة: يجب أن يكون العنصر للحصول على قيمة السمة العنصر الأخير من التعبير ، وإضافة "$" بعد العنصر الأخير. على سبيل المثال ، يمثل تعبير "Root/A/B/D $ $ .id $" مجموعة من سمات معرف عناصر D المتعددة تحت عنصر الجذر ، وتحت العنصر A ، وتحت عنصر B.
يوضح الشكل XML أعلاه ، المقابل لشجرة بناء الجملة المجردة ، والهيكل المحتمل في الشكل:
لنلقي نظرة على الكود المحدد أدناه:
1. تحديد السياق:
نسخة الكود كما يلي:
/**
* السياق ، يستخدم لاحتواء بعض المعلومات العالمية المطلوبة من قبل المترجم
* param {string} filePathName [مسار واسم XML الذي يجب قراءته]
*/
وظيفة سياق (filePathName) {
// العنصر السابق المعالج
this.preele = null ؛
// كائن مستند XML
this.document = xmlutil.getroot (filePathName) ؛
}
context.prototype = {
// إعادة توحيد السياق
Reinit: Function () {
this.preele = null ؛
} ،
/**
* طرق الاستخدام العام لكل تعبير
* احصل على العنصر الحالي وفقًا لاسم العنصر الأصل والعنصر الحالي
* param {element} pele [العنصر الأصل]
* param {string} elename [اسم العنصر الحالي]
* regurn {element | null} [تم العثور على العنصر الحالي]
*/
GetNowele: Function (Pele ، Elename) {
var tempnodelist = pele.childnodes ؛
فار نولي.
لـ (var i = 0 ، len = tempnodelist.length ؛ i <len ؛ i ++) {
if ((nowele = tempnodelist [i]). nodetype === 1)
if (nowele.nodename === Elename)
إرجاع NOMEELE.
}
العودة لاغية.
} ،
getPreele: function () {
إرجاع هذا.
} ،
setPreele: function (preele) {
this.preele = preele ؛
} ،
getDocument: function () {
إرجاع هذا.
}
} ؛
في السياق ، استخدمت كائن أداة xmlutil للحصول على XMLDOM. أدناه أنا أستخدم Dompaser of DOM3. قد لا تدعم بعض المتصفحات. الرجاء استخدام المتصفح الأساسي:
نسخة الكود كما يلي:
كائن الأداة //
// تحليل XML للحصول على كائن المستند المقابل
var xmlutil = {
getRoot: وظيفة (filePathName) {
var parser = new Domparser () ؛
var xmldom = parser.parsefromString ('<rout id = "rootId"> <a> <b> <c name = "testc"> 12345 </c> <d id = "1"> d1 </d> <d id = "2"> d2 </ d> <d id = "3" id = "4"> d4 </d> </b> </a> </rout> '،' text/xml ') ؛
إرجاع xmldom ؛
}
} ؛
هنا هو رمز المترجم المترجم:
نسخة الكود كما يلي:
/**
* يتم استخدام العناصر كمترجمين فوريين يتوافقون مع غير الطرفي لتفسير وتنفيذ العناصر الوسيطة
* param {string} elename [اسم العنصر]
*/
وظيفة elementexpression (elename) {
this.eles = [] ؛
this.Elename = elename ؛
}
elementExpression.prototype = {
addele: وظيفة (elename) {
this.eles.push (elename) ؛
العودة صحيح.
} ،
removeEle: وظيفة (eLe) {
لـ (var i = 0 ، len = this.eles.length ؛ i <len ؛ i ++) {
إذا (ele === this.eles [i])
this.eles.splice (i-- ، 1) ؛
}
العودة صحيح.
} ،
التفسير: الوظيفة (السياق) {
// أولاً ، قم بإخراج العنصر الحالي في السياق كعنصر الأصل
// ابحث عن عنصر XML المقابل لاسم العنصر الحالي وقم بإعادته إلى السياق
var pele = context.getPreele () ؛
if (! pele) {
// يشير إلى أنه يتم الآن الحصول على عنصر الجذر
context.setPreele (context.getDocument (). documentElement) ؛
} آخر {
// احصل على العنصر الحالي استنادًا إلى اسم العنصر الأصل والعنصر المراد تفتيشه
var nowele = context.getNowele (pele ، this.Elename) ؛
// ضع العنصر الذي تم استرداده حاليًا في السياق
context.setPreele (novele) ؛
}
var ss ؛
// حلقة لاستدعاء طريقة التفسير للعنصر الطفل
لـ (var i = 0 ، len = this.eles.length ؛ i <len ؛ i ++) {
ss = this.eles [i] .Interpret (السياق) ؛
}
// إرجاع نتيجة التفسير للترجم المترجم الأخير. بشكل عام ، فإن المترجم الفوري الأخير هو مترجم Terminator.
إرجاع SS ؛
}
} ؛
/**
* يتم استخدام العناصر كمترجم مترابطة المقابلة للممنف
* param {string} اسم [اسم العنصر]
*/
وظيفة elementTerMinalExpression (الاسم) {
this.Elename = name ؛
}
elementTerMinalexpression.prototype = {
التفسير: الوظيفة (السياق) {
var pele = context.getPreele () ؛
var ele = null ؛
if (! pele) {
eLe = context.getDocument (). documentElement ؛
} آخر {
eLe = context.getNowele (pele ، this.Elename) ؛
context.setpreele (eLE) ؛
}
// احصل على قيمة العنصر
إرجاع ele.firstchild.nodevalue ؛
}
} ؛
/**
* يتم استخدام السمة كمترجم متروك المقابل للممنف
* param {string} propname [اسم السمة]
*/
وظيفة propertyterminalexpression (propname) {
this.propName = propname ؛
}
propertyterminalexpression.prototype = {
التفسير: الوظيفة (السياق) {
// احصل على قيمة سمة العنصر الأخير مباشرة
return context.getPreele (). getAttribute (this.propName) ؛
}
} ؛
دعونا أولاً نلقي نظرة على كيفية استخدام المترجم المترجم للحصول على قيمة عنصر واحد:
نسخة الكود كما يلي:
وظيفة void () {
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 elementTerminalexpression ('c') ؛
// مزيج
root.addele (aele) ؛
AELE.Addele (BELE) ؛
BELE.Addele (CELE) ؛
console.log ('قيمة C هي =' + root.Interpret (c)) ؛
} () ؛
الإخراج: قيمة C هي = 12345
ثم نستخدم الكود أعلاه للحصول على قيمة سمة عنصر واحد:
نسخة الكود كما يلي:
وظيفة void () {
var c = new Context () ؛
// تريد الحصول على سمة المعرف لعنصر D ، أي قيمة التعبير التالي: "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 PropertyMerMinalexpression ('name') ؛
// مزيج
root.addele (aele) ؛
AELE.Addele (BELE) ؛
BELE.Addele (CELE) ؛
cele.addele (prop) ؛
console.log ('قيمة اسم خاصية C هي =' + root.Interpret (c)) ؛
// إذا كنت ترغب في استخدام نفس السياق والحلل بشكل مستمر ، فأنت بحاجة إلى إعادة تهيئة كائن السياق
// على سبيل المثال ، تحتاج إلى إعادة قيمة اسم السمة مرة أخرى على التوالي ، بالطبع يمكنك إعادة تجميع العناصر
// RE-PARSE ، طالما يتم استخدام نفس السياق ، يحتاج كائن السياق إلى إعادة صياغة
c.reinit () ؛
console.log ('قيمة اسم خاصية Reget C هي =' + root.Interpret (c)) ؛
} () ؛
الإخراج: قيمة اسم السمة لـ C هي = testc استرجع قيمة اسم السمة C هي = testc
يشرح:
1. وظيفة وضع المترجم:
يستخدم نمط المترجم كائنات مترجم لتمثيل ومعالجة قواعد بناء الجملة المقابلة. بشكل عام ، يتعامل المترجم المترجم مع قاعدة بناء الجملة. من الناحية النظرية ، طالما أن التعبيرات المتوافقة مع بناء الجملة يمكن تمثيلها بواسطة كائن المترجم ويمكن أن تشكل شجرة بناء جملة مجردة ، يمكن استخدام نمط المترجم للتعامل معه.
2. قواعد بناء الجملة والمترجمين الفوريين
هناك مراسلات بين قواعد بناء الجملة والمترجمين الفوريين. بشكل عام ، يتعامل المترجم المترجم مع قاعدة بناء الجملة ، لكن العكس غير صحيح. يمكن أن يكون لقاعدة بناء الجملة تفسيرات ومعالجة متعددة ، أي يمكن أن تتوافق قاعدة بناء الجملة مع متعددين من المترجمين الفوريين.
3. السياق المشترك
يلعب السياق دورًا مهمًا للغاية في وضع المترجم الفوري. لأن السياق ينتقل إلى جميع المترجمين الفوريين. لذلك ، يمكن تخزين حالة المترجم المترجم والوصول إليها في السياق. على سبيل المثال ، يمكن للمترجم السابق تخزين بعض البيانات في السياق ، ويمكن للمترجم الأخير الحصول على هذه القيم.
بالإضافة إلى ذلك ، يمكن تمرير بعض البيانات خارج المترجم المترجم عبر السياق ، لكن المترجم يحتاجه ، وبعض البيانات العامة العالمية.
هناك أيضًا وظيفة في السياق ، وهي أنه يمكن أن توفر الوظائف الشائعة لجميع كائنات المترجم ، على غرار مجموعات الكائنات ، بدلاً من استخدام الميراث للحصول على وظائف شائعة ، والتي يمكن استدعاؤها في كل كائن مترجم.
4. من سيبني شجرة بناء جملة مجردة
في المثال السابق ، من المزعج للغاية إنشاء شجرة بناء الجملة المجردة يدويًا على جانب العميل ، ولكن في وضع المترجم المترجم ، لا يشارك هذا الجزء من الوظيفة ، وهو مسؤول فقط عن تفسير شجرة بناء الجملة المجردة ومعالجتها. سنقدم أنه يمكننا توفير محلل لتحويل التعبيرات إلى أشجار بناء الجملة المجردة.
هناك مشكلة أخرى ، أي ، يمكن أن تتوافق قاعدة بناء الجملة مع كائنات مترجم متعددة ، أي يمكن تحويل نفس العنصر إلى كائنات مترجم متعددة ، مما يعني أن التعبير نفسه يمكن أن يشكل شجرة بناء جملة غير ضرورية ، مما يجعل من الصعب أيضًا بناء شجرة بناء جملة مجردة وأن عبء العمل كبير جدًا.
5. من المسؤول عن شرح العملية
طالما تم تعريف شجرة بناء الجملة المجردة ، يجب أن يكون المترجم مسؤولاً عن تفسير وتنفيذ. على الرغم من وجود قواعد بناء جملة مختلفة ، إلا أن المترجم غير مسؤول عن اختيار كائن مترجم يجب استخدامه لتفسير قواعد بناء جملة التنفيذ. يتم الانتهاء من وظيفة تحديد المترجم المترجم عند إنشاء شجرة بناء جملة مجردة.
6. ترتيب Call of Troupreter Mode
1) إنشاء كائن سياق
2) إنشاء كائنات مترجم متعددة وجمع بين أشجار بناء الجملة المجردة
3) استدعاء عملية تفسير كائن المترجم
3.1) تخزين والوصول إلى حالة المترجم المترجم من خلال السياق.
بالنسبة للكائنات المترجمة غير المحطة ، استدعاء بشكل متكرر الكائن الفرعي الذي يحتوي عليه.
جوهر نمط المترجم: *التنفيذ المنفصل ، تفسير التنفيذ *
تستخدم وحدة المترجم المترجم كائن مترجم لمعالجة قاعدة بناء الجملة لفصل وظائف معقدة ؛ ثم يختار الوظائف التي يجب تنفيذها ، ويجمع بين هذه الوظائف في شجرة بناء جملة مجردة تحتاج إلى تفسير وتنفيذ ؛ ثم يفسر التنفيذ وفقًا لشجرة بناء الجملة المجردة لتنفيذ الوظائف المقابلة.
على السطح ، يركز وضع المترجم المترجم على معالجة بناء الجملة المخصص الذي لا نستخدمه عادة ؛ ولكن في جوهرها ، تكون فكرة وضع المترجم المترجم هي الفصل ، والتغليف ، والتبسيط ، وهي نفس أوضاع العديد من الأوضاع.
على سبيل المثال ، يمكن استخدام وضع المترجم المترجم لمحاكاة وظيفة وضع الحالة. إذا تم تبسيط بناء الجملة الذي سيتم معالجته بواسطة وضع المترجم المترجم إلى علامة حالة واحدة فقط ، يُعتبر المترجم كائن معالجة للحالة. لنفس بناء الجملة الذي يمثل الحالة ، يمكن أن يكون هناك العديد من المترجمين الفوريين غير المستخدمين ، أي أن هناك العديد من الكائنات ذات حالات المعالجة المختلفة. عند إنشاء شجرة بناء جملة مجردة ، يتم تبسيطها لإنشاء المترجم المقابل بناءً على علامة الحالة ، وليس هناك حاجة لبناء شجرة.
وبالمثل ، يمكن أن يحاكي وضع المترجم المترجم وظيفة تنفيذ وضع السياسة ، ووظيفة وضع الديكور ، وما إلى ذلك ، خاصةً عملية محاكاة وظيفة وضع الديكور ، وعملية بناء شجرة بناء الجملة المجردة تتوافق بشكل طبيعي مع عملية الجمع بين الديكور.
عادةً ما يكون وضع المترجم سريعًا (في الغالب بطيئًا جدًا) ، وتصحيح الأخطاء الصحيح أمر صعب (على الرغم من أن تصحيح الأخطاء أمر صعب ، إلا أنه يقلل بالفعل من إمكانية وجود أخطاء) ، لكن مزاياه واضحة. يمكن أن تتحكم بشكل فعال في تعقيد الواجهات بين الوحدات. بالنسبة للوظائف التي لها تردد تنفيذ منخفض ولكن التردد العالي الكود بدرجة كافية ، ومتنوعة للغاية ، فإن المترجمين الفوريين مناسبة للغاية للوسائط. بالإضافة إلى ذلك ، يتمتع المترجم المترجم بميزة أخرى أقل وضوحًا ، وهو أنه يمكن أن تكون متعددة اللغات المترتبة على المنصة.
مزايا وعيوب وضع المترجم:
ميزة:
1. من السهل تنفيذ بناء الجملة
في وضع المترجم المترجم ، يتم تفسير قاعدة بناء الجملة باستخدام كائن مترجم لتفسير التنفيذ. لتنفيذ المترجم ، تصبح الوظيفة بسيطة نسبيًا. ما عليك سوى النظر في تنفيذ قاعدة بناء الجملة هذه ، ولا داعي للقلق بشأن أي شيء آخر. 2. من السهل توسيع بناء الجملة الجديدة
يرجع ذلك بالتحديد إلى الطريقة التي يكون بها كائن مترجم مسؤول عن قاعدة بناء الجملة بأن تمديد بناء الجملة الجديد أمر سهل للغاية. تم توسيع بناء الجملة الجديد ، وتحتاج فقط إلى إنشاء كائن المترجم المترجم المقابل واستخدام كائن المترجم الجديد هذا عند إنشاء شجرة بناء جملة مجردة.
عيب:
غير مناسب لبناء بناء الجملة المعقدة
إذا كان بناء الجملة معقدًا بشكل خاص ، فإن عمل بناء شجرة بناء الجملة المجردة المطلوبة بنمط المترجم أمر صعب للغاية ، ومن الممكن إنشاء أشجار بناء جملة متعددة. لذلك ، فإن نمط المترجم غير مناسب لبناء بناء الجملة المعقدة. قد يكون من الأفضل استخدام محلل بناء الجملة أو مولد التحويل البرمجي.
متى تستخدمه؟
عندما تكون هناك لغة تحتاج إلى تفسير وتنفيذ ، ويمكن تمثيل الجمل في تلك اللغة كشجرة بناء جملة مجردة ، يمكنك التفكير في استخدام نمط المترجم.
عند استخدام وضع المترجم المترجم ، هناك ميزتان آخران يجب مراعاة. واحد هو أن بناء الجملة يجب أن يكون بسيطا نسبيا. بناء الجملة المسؤول جدًا غير مناسب لاستخدام وضع المترجم المترجم. والآخر هو أن متطلبات الكفاءة ليست عالية جدًا ومتطلبات الكفاءة عالية جدًا ، وهي غير مناسبة للاستخدام.
كانت المقدمة السابقة هي كيفية الحصول على قيمة عنصر واحد وقيمة سمة عنصر واحد. دعونا نلقي نظرة على كيفية الحصول على قيم عناصر متعددة ، وكذلك قيم أسماء بعضها البعض في عناصر متعددة ، وكل الاختبارات السابقة هي الأشجار المجردة المصطنع. نقوم أيضًا بتنفيذ المحلل المحلل البسيط التالي لتحويل التعبيرات التي تتوافق مع بناء الجملة المحدد أعلاه في أشجار بناء الجملة المجردة للمترجم المطلوب أعلاه: قمت بنشر الكود مباشرة:
نسخة الكود كما يلي:
// اقرأ قيم عناصر أو سمات متعددة
(وظيفة () {
/**
* السياق ، يستخدم لاحتواء بعض المعلومات العالمية المطلوبة من قبل المترجم
* param {string} filePathName [مسار واسم XML الذي يجب قراءته]
*/
وظيفة سياق (filePathName) {
// عناصر متعددة تمت معالجتها في السابق
this.preeles = [] ؛
// كائن مستند XML
this.document = xmlutil.getroot (filePathName) ؛
}
context.prototype = {
// إعادة توحيد السياق
Reinit: Function () {
this.preeles = [] ؛
} ،
/**
* طرق الاستخدام العام لكل تعبير
* احصل على العنصر الحالي وفقًا لاسم العنصر الأصل والعنصر الحالي
* param {element} pele [العنصر الأصل]
* param {string} elename [اسم العنصر الحالي]
* regurn {element | null} [تم العثور على العنصر الحالي]
*/
getNoweles: وظيفة (بيليه ، elename) {
عناصر var = [] ؛
var tempnodelist = pele.childnodes ؛
فار نولي.
لـ (var i = 0 ، len = tempnodelist.length ؛ i <len ؛ i ++) {
if ((nowele = tempnodelist [i]). nodetype === 1) {
if (nowele.nodename === Elename) {
elements.push (nowele) ؛
}
}
}
عناصر العودة ؛
} ،
getPreeles: function () {
إرجاع هذا.
} ،
setPreeles: function (noweles) {
this.preeles = noweles ؛
} ،
getDocument: function () {
إرجاع هذا.
}
} ؛
كائن الأداة //
// تحليل XML للحصول على كائن المستند المقابل
var xmlutil = {
getRoot: وظيفة (filePathName) {
var parser = new Domparser () ؛
var xmldom = parser.parsefromString ('<rout id = "rootId"> <a> <b> <c name = "testc"> 12345 </c> <d id = "1"> d1 </d> <d id = "2"> d2 </ d> <d id = "3" id = "4"> d4 </d> </b> </a> </rout> '،' text/xml ') ؛
إرجاع xmldom ؛
}
} ؛
/**
* يتم استخدام العناصر كمترجمين فوريين يتوافقون مع غير الطرفي لتفسير وتنفيذ العناصر الوسيطة
* param {string} elename [اسم العنصر]
*/
وظيفة elementexpression (elename) {
this.eles = [] ؛
this.Elename = elename ؛
}
elementExpression.prototype = {
addele: وظيفة (elename) {
this.eles.push (elename) ؛
العودة صحيح.
} ،
removeEle: وظيفة (eLe) {
لـ (var i = 0 ، len = this.eles.length ؛ i <len ؛ i ++) {
if (ele === this.eles [i]) {
this.eles.splice (i-- ، 1) ؛
}
}
العودة صحيح.
} ،
التفسير: الوظيفة (السياق) {
// أولاً ، قم بإخراج العنصر الحالي في السياق كعنصر الأصل
// ابحث عن عنصر XML المقابل لاسم العنصر الحالي وقم بإعادته إلى السياق
var peles = context.getPreeles () ؛
var ele = null ؛
var noweles = [] ؛
if (! peles.length) {
// يشير إلى أنه يتم الآن الحصول على عنصر الجذر
eLe = context.getDocument (). documentElement ؛
peles.push (eLe) ؛
context.setPreeles (peles) ؛
} آخر {
var tempele ؛
لـ (var i = 0 ، len = peles.length ؛ i <len ؛ i ++) {
tempele = peles [i] ؛
noweles = noweles.concat (context.getNoweles (tempele ، this.enename)) ؛
// توقف إذا وجدت واحدة
إذا (noweles.length) استراحة ؛
}
context.setPreeles ([noweles [0]]) ؛
}
var ss ؛
// حلقة لاستدعاء طريقة التفسير للعنصر الطفل
لـ (var i = 0 ، len = this.eles.length ؛ i <len ؛ i ++) {
ss = this.eles [i] .Interpret (السياق) ؛
}
إرجاع SS ؛
}
} ؛
/**
* يتم استخدام العناصر كمترجم مترابطة المقابلة للممنف
* param {string} اسم [اسم العنصر]
*/
وظيفة elementTerMinalExpression (الاسم) {
this.Elename = name ؛
}
elementTerMinalexpression.prototype = {
التفسير: الوظيفة (السياق) {
var peles = context.getPreeles () ؛
var ele = null ؛
if (! peles.length) {
eLe = context.getDocument (). documentElement ؛
} آخر {
eLe = context.getNoweles (peles [0] ، this.Elename) [0] ؛
}
// احصل على قيمة العنصر
إرجاع ele.firstchild.nodevalue ؛
}
} ؛
/**
* يتم استخدام السمة كمترجم متروك المقابل للممنف
* param {string} propname [اسم السمة]
*/
وظيفة propertyterminalexpression (propname) {
this.propName = propname ؛
}
propertyterminalexpression.prototype = {
التفسير: الوظيفة (السياق) {
// احصل على قيمة سمة العنصر الأخير مباشرة
return context.getPreeles () [0] .getAttribute (this.propName) ؛
}
} ؛
/**
* تُستخدم سمات متعددة كمترجم مترجمي يتوافق مع المنهي
* param {string} propname [اسم السمة]
*/
وظيفة propertysterMinAlexpression (propname) {
this.propName = propname ؛
}
propertysterMinaSpression.prototype = {
التفسير: الوظيفة (السياق) {
var eles = context.getPreeles () ؛
var ss = [] ؛
لـ (var i = 0 ، len = eles.length ؛ i <len ؛ i ++) {
ss.push (eles [i] .getAttribute (this.propName)) ؛
}
إرجاع SS ؛
}
} ؛
/**
* كائن معالجة التفسير مع عناصر متعددة كمقصات
* param {[type]} الاسم [الوصف]
*/
وظيفة elementsterminalexpression (الاسم) {
this.Elename = name ؛
}
elementsterminalexpression.prototype = {
التفسير: الوظيفة (السياق) {
var peles = context.getPreeles () ؛
var noweles = [] ؛
لـ (var i = 0 ، len = peles.length ؛ i <len ؛ i ++) {
noweles = noweles.concat (context.getNoweles (peles [i] ، this.Elename)) ؛
}
var ss = [] ؛
لـ (i = 0 ، len = noweles.length ؛ i <len ؛ i ++) {
ss.push (noweles [i] .firstchild.nodevalue) ؛
}
إرجاع SS ؛
}
} ؛
/**
* يتم تفسير عناصر متعددة على أنها غير الطرفية
*/
وظيفة elementsexpression (الاسم) {
this.Elename = name ؛
this.eles = [] ؛
}
elementsexpression.prototype = {
التفسير: الوظيفة (السياق) {
var peles = context.getPreeles () ؛
var noweles = [] ؛
لـ (var i = 0 ، len = peles.length ؛ i <len ؛ i ++) {
noweles = noweles.concat (context.getNoweles (peles [i] ، this.Elename)) ؛
}
context.setPreeles (noweles) ؛
var ss ؛
لـ (i = 0 ، len = this.eles.length ؛ i <len ؛ i ++) {
ss = this.eles [i] .Interpret (السياق) ؛
}
إرجاع SS ؛
} ،
Addele: Function (ele) {
this.eles.push (eLe) ؛
العودة صحيح.
} ،
removeEle: وظيفة (eLe) {
لـ (var i = 0 ، len = this.eles.length ؛ i <len ؛ i ++) {
if (ele === this.eles [i]) {
this.eles.splice (i-- ، 1) ؛
}
}
العودة صحيح.
}
} ؛
وظيفة void () {
// "Root/A/B/D $"
var c = سياق جديد ('teanster.xml') ؛
var root = new elementExpression ('root') ؛
var aele = new elementExpression ('a') ؛
var bele = new elementExpression ('b') ؛
var dele = elementsterminalexpression ('d') ؛
root.addele (aele) ؛
AELE.Addele (BELE) ؛
BELE.Addele (DELE) ؛
var ss = root.Interpret (c) ؛
لـ (var i = 0 ، len = ss.length ؛ i <len ؛ i ++) {
console.log ('d value is =' + ss [i]) ؛
}
} () ؛
وظيفة void () {
// a/b/d $ .id $
var c = سياق جديد ('teanster.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) ؛
لـ (var i = 0 ، len = ss.length ؛ i <len ؛ i ++) {
console.log ('d قيمة معرف الخاصية هي =' + ss [i]) ؛
}
} () ؛
// arser
/**
* أفكار تنفيذ المحلل
* 1. تحلل التعبيرات التي يقرها العميل ، وتحللها إلى عناصر واحدة تلو الأخرى ، واستخدم نموذجًا تحليليًا مقابلًا لتغليف بعض المعلومات حول هذا العنصر.
* 2. قم بتحويله إلى كائن المحلل المقابل بناءً على معلومات كل عنصر.
* 3. الجمع بين هذه الكائنات المحلل من أجل الحصول على شجرة بناء جملة مجردة.
*
* لماذا لا تندمج 1 و 2 ، وتحلل عنصرًا مباشرة وتحويله إلى كائن المحلل المقابل؟
* 1. الفصل الوظيفي ، لا تجعل وظائف الطريقة معقدة للغاية.
* 2. للتعديل المستقبلي والتمديد ، أصبح بناء الجملة بسيطًا الآن ، لذلك ليس هناك ما يجب مراعاته عند التحويل إلى كائن محلل ، وليس من الصعب التحويل مباشرة ، ولكن إذا كان بناء الجملة معقدًا ، فسيكون التحويل المباشر فوضويًا للغاية.
*/
/**
* تستخدم لتغليف السمات المقابلة لكل عنصر محلل
*/
وظيفة parsermodel () {
// ما إذا كانت قيمة واحدة
this.singlevalue ؛
// سواء كانت سمة أو سمة أو عنصر
this.propertyvalue ؛
// ما إذا كان يجب إنهاء
هذا.
}
parsermodel.prototype = {
isend: function () {
إرجاع هذا.
} ،
Setend: Function (end) {
this.end = نهاية ؛
} ،
issinglevalue: function () {
إرجاع this.singlevalue ؛
} ،
SetSinglevalue: وظيفة (OneValue) {
this.singlevalue = OneValue ؛
} ،
isPropertyValue: function () {
إرجاع هذا.
} ،
setPropertyValue: Function (propertyValue) {
this.propertyvalue = propertyValue ؛
}
} ؛
var parser = function () {
var backleash = '/' ؛
var dot = '.' ؛
var dollar = '$' ؛
// سجل أسماء العناصر التي يجب تحليلها وفقًا لترتيب التحلل
var listele = null ؛
// ابدأ الخطوة الأولى ------------------------------------------------------------------------------------------------------------------------
/**
* تمرير في تعبير سلسلة ، ثم تحليله ودمجه في شجرة بناء جملة مجردة
* param {string} expr [صف تعبير السلسلة لأخذ القيمة]
* regurn {object} [شجرة بناء الجملة المجردة المقابلة]
*/
وظيفة parsemappath (expr) {
// قسّم السلسلة أولاً وفقًا لـ "/"
var tokenizer = expr.split (رد فعل عكسي) ؛
// جدول القيم المتحللة
var mappath = {} ؛
var OnePath ، Elename ، Propname ؛
var dotindex = -1 ؛
لـ (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) {
// هذا يعني أنك تريد الحصول على قيمة السمة ، لذلك قسّمها وفقًا لـ ".
// الأول هو اسم العنصر ، والثاني هو اسم السمة
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) ؛
}
استراحة؛
}
}
إرجاع mappath
}
/**
* اضبط اسم العنصر ليتم تحليله وفقًا للموقع والاسم المتحللين
* param {boolean} نهاية [هل هو الأخير]
* param {string} ele [اسم العنصر]
* param {boolean} propertyValue [ما إذا كان يجب أخذ العقار]
* param {object} mappath [اضبط اسم العنصر الذي يجب تحليله ، وجدول نموذج التحليل المقابل للعنصر]
*/
دالة setParsepath (end ، ele ، propertyvalue ، mappath) {
var pm = new parsermodel () ؛
PM.SetEnd (end) ؛
// إذا كان الرمز "$" ليس قيمة
pm.setsinglevalue (! (ele.indexof (الدولار)> = 0)) ؛
PM.SetPropertyValue (PropertyValue) ؛
// إزالة "$"
ele = ele.replace (الدولار ، '') ؛
mappath [ele] = pm ؛
listele.push (eLe) ؛
}
// ابدأ الخطوة الثانية ----------------------------------------------------------------------------------------------------------------------
/**
* قم بتحويل اسم العنصر المتحلل إلى كائن مترجم مترجمي المقابل وفقًا للنموذج التحليلي المقابل.
* param {object} mappath [اسم العنصر المتحلل ليتم تحليله ، ونموذج التحليل المقابل للعنصر]
* return {array} [تحويل كل عنصر إلى صفيف من كائنات المترجمين المقابلة]
*/
وظيفة mappath2Interpreter (mappath) {
var list = [] ؛
var pm ، المفتاح ؛
var obj = null ؛
// من الضروري تحويله إلى كائنات مترجم بترتيب التحلل
لـ (var i = 0 ، len = listele.length ؛ i <len ؛ i ++) {
المفتاح = Listele [i] ؛
PM = mappath [مفتاح] ؛
// ليس آخر واحد
if (! pm.isend ()) {
إذا (pm.issinglevalue ())
// قيمة وتحويل
OBJ = new elementExpression (مفتاح) ؛
آخر
// قيم متعددة ، التحويل
OBJ = new elementSexpression (مفتاح) ؛
} آخر {
// إنه الأخير
// هي قيمة السمة
if (pm.ispropertyvalue ()) {
إذا (pm.issinglevalue ())
OBJ = propertymerTerminess (مفتاح) ؛
آخر
OBJ = new propertysterMinAlexpression (مفتاح) ؛
// خذ قيمة العنصر
} آخر {
إذا (pm.issinglevalue ())
OBJ = new elementTerMinalexpression (مفتاح) ؛
آخر
OBJ = ElementSretherMiness (مفتاح) ؛
}
}
list.push (obj) ؛
}
قائمة العودة
}
// ابدأ الخطوة الثالثة ----------------------------------------------------------------------------------------------------------------------
/**
* بناء شجرة بناء الجملة مجردة
* param {[type]} قائمة [تحويل كل عنصر إلى صفيف من كائنات المترجمين المقابلة]
* regurn {[type]} [الوصف]
*/
وظيفة BuildTree (قائمة) {
// الكائن الأول ، أيضًا الكائن الذي تم إرجاعه ، هو جذر شجرة بناء الجملة المجردة
var returnReadxMlexpr = null ؛
// تحديد الكائن السابق
var preeadxmlexpr = null ؛
var readxml ، ele ، eles ؛
لـ (var i = 0 ، len = list.length ؛ i <len ؛ i ++) {
readxml = list [i] ؛
// الوصف هو العنصر الأول
if (preeadxmlexpr ==== null) {
preadxmlexpr = readxml ؛
returnReadxMlexpr = readxml ؛
// أضف العنصر إلى الكائن السابق وضبط الكائن على Oldre
// كعقدة الأصل للكائن التالي
} آخر {
if (preeadxmlexpr مثيل elementExpression) {
eLe = preeadxmlexpr ؛
ele.addele (readxml) ؛
preadxmlexpr = readxml ؛
} آخر إذا (preeadxmlexpr مثيل elementSexpression) {
eles = preeadxmlexpr ؛
eles.addele (readxml) ؛
preadxmlexpr = readxml ؛
}
}
}
إرجاع ReturnReadxMlexpr ؛
}
يعود {
// الطريقة العامة
تحليل: وظيفة (expr) {
listele = [] ؛
var mappath = parsemappath (expr) ؛
var list = mappath2Interpreter (mappath) ؛
إرجاع BuildTree (قائمة) ؛
}
} ؛
} () ؛
وظيفة void () {
// إعداد السياق
var c = سياق جديد ('teanster.xml') ؛
// احصل على شجرة بناء الجملة المجردة عن طريق تحليلها
var readxmlexpr = parser.parse ('root/a/b/d $ .id $') ؛
// اطلب التحليل والحصول على قيمة الإرجاع
var ss = readxmlexpr.interpret (c) ؛
console.log ('------------ parsing --------------') ؛
لـ (var i = 0 ، len = ss.length ؛ i <len ؛ i ++) {
console.log ('d قيمة معرف الخاصية هي =' + 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--------------