
مرحبا بالجميع!
نواصل سلسلة من المقالات حول تطوير محرك المتصفح.
في هذه المقالة سأخبرك بكيفية إنشاء أسرع محلل HTML مع DOM. سنلقي نظرة على مواصفات HTML ولماذا يكون سيئًا فيما يتعلق بالأداء واستهلاك الموارد عند تحليل HTML.
مع هذا الموضوع ، أبلغت عن الماضي HighLoad ++. لا يمكن للجميع حضور المؤتمر ، بالإضافة إلى أن المقالة تحتوي على مزيد من التفاصيل.
أفترض أن القارئ لديه معرفة أساسية بـ HTML: العلامات والعقد والعناصر ومساحة الاسم.
مواصفات HTML
قبل أن تبدأ في لمس تطبيق محلل HTML ، تحتاج إلى فهم ما هي مواصفات HTML التي يجب أن تؤمن بها.
هناك نوعان من مواصفات HTML:
- WHATWG
- آبل وموزيلا وجوجل ومايكروسوفت
- W3c
بطبيعة الحال ، وقع الاختيار على قادة الصناعة - WHATWG
. مستوى المعيشة ، الشركات الكبيرة لكل منها محرك متصفح / متصفح خاص بها.
حدث: للأسف ، لا يتم فتح الروابط إلى المواصفات من روسيا. على ما يبدو ، "صدى الحرب" مع البرقيات.
عملية تحليل HTML
يمكن تقسيم عملية بناء شجرة HTML إلى أربعة أجزاء:
- فك
- المعالجة
- الرمز
- بناء شجرة
نحن نعتبر كل مرحلة على حدة.
فك
يقبل الرمز المميز أحرف Unicode (نقاط الرمز) كمدخل. وفقًا لذلك ، نحتاج إلى تحويل دفق البايت الحالي إلى أحرف Unicode. للقيام بذلك ، استخدم مواصفات الترميز .
إذا كان لدينا HTML بترميز غير معروف (بدون رأس HTTP) ، فإننا نحتاج إلى تحديده قبل بدء فك التشفير. للقيام بذلك ، سوف نستخدم خوارزمية تشفير التشفير .
إذا كان لفترة وجيزة جدًا ، فإن جوهر الخوارزمية هو أننا ننتظر 500
أو أول 1024
من دفق البايت ونشغل خوارزمية دفق البايت المسبقة لتحديد ترميزها الذي يحاول العثور على <meta>
مع السمات http-equiv
أو content
أو charset
ويحاول فهم ما يشير إليه ترميز HTML.
تحدد مواصفات Encoding
الحد الأدنى من مجموعة الترميزات المدعومة بواسطة محرك المتصفح (21 في المجموع): UTF-8 و ISO-8859-2 و ISO-8859-7 و ISO-8859-8 و windows-874 و windows-1250 و windows-1251 و windows -1252 و windows-1254 و windows-1255 و windows-1256 و windows-1257 و windows-1258 و gb18030 و Big5 و ISO-2022-JP و Shift_JIS و EUC-KR و UTF-16BE و UTF-16LE و x-user محدد.
المعالجة
بعد فك تشفير وحدات البايت إلى أحرف Unicode ، نحتاج إلى "تنظيف". أي ، استبدل جميع أحرف إرجاع حرف ( \r
) متبوعًا بحرف تغذية سطر ( \n
) بحرف إرجاع حرف ( \r
). ثم استبدل جميع أحرف الإرجاع بحرف سطر جديد ( \n
).
موصوف في المواصفات. أي ، \r\n
=> \r
، \r
=> \n
.
ولكن ، في الواقع ، لا أحد يفعل ذلك. اجعله أسهل:
إذا حصلت على حرف إرجاع حرف ( \r
) ، فابحث لمعرفة ما إذا كان هناك حرف تغذية سطر ( \n
). إذا كان هناك ، فإننا نغير الحرفين إلى حرف تغذية السطر ( \n
) ، إن لم يكن ، فإننا نغير الحرف الأول فقط ( \r
) إلى تغذية السطر ( \n
).
هذا يكمل معالجة البيانات الأولية. نعم ، تحتاج فقط للتخلص من رموز إرجاع حامل الخراطيش حتى لا تقع في الرمز المميز. لا يتوقع الرمز المميز ولا يعرف ماذا يفعل برمز إرجاع حامل الخراطيش.
أخطاء التحليل
بحيث لا توجد أسئلة في المستقبل ، يجب أن تخبر على الفور عن
( parse error
).
لا شيء خطأ حقا. يبدو ذلك مهددًا ، ولكن في الواقع هذا مجرد تحذير أننا كنا نتوقع واحدًا ، ولكن لدينا آخر.
لن يوقف خطأ التحليل معالجة البيانات أو بناء الشجرة. هذه رسالة تشير إلى أنه ليس لدينا HTML صالح.
يمكن الحصول على خطأ Parsig للأزواج البديلة ، \0
، موقع العلامة غير صحيح ، <!DOCTYPE>
غير صحيح وجميع أنواع الأشياء الأخرى.
بالمناسبة ، بعض أخطاء التحليل تؤدي إلى عواقب. على سبيل المثال ، إذا حددت "سيئة" <!DOCTYPE>
، فسيتم وضع علامة على شجرة HTML على أنها QUIRKS
منطق بعض وظائف DOM.
الرمز
كما ذكرنا سابقًا ، يقبل الرمز المميز أحرف Unicode كمدخل. هذه آلة دولة بها 80
ولاية. في كل حالة ، شروط أحرف Unicode. اعتمادًا على الحرف الذي تم تلقيه ، قد يستخدم الرمز المميز:
- تغيير حالتك
- إنشاء رمز مميز وتغيير الحالة
- لا تفعل شيئًا ، انتظر الحرف التالي
ينشئ الرمز المميز ستة أنواع من الرموز المميزة: DOCTYPE و Start Tag و End Tag و Comment و Character و End-Of-File. التي تدخل مرحلة بناء شجرة.
يشار إلى أن الرمز المميز لا يعرف عن جميع حالاته ، ولكن حيث حوالي 40٪ (مأخوذة من السقف على سبيل المثال). "لماذا الباقي؟" - أنت تسأل. حوالي 60٪ المتبقية تعرف مرحلة بناء شجرة.
يتم ذلك من أجل تحليل العلامات مثل <textarea>
و <style>
و <script>
و <title>
بشكل صحيح. هذا هو ، عادة ، العلامات التي لا نتوقع فيها علامات أخرى ، ولكن فقط إغلاق أنفسنا.
على سبيل المثال ، لا يمكن أن تحتوي <title>
على علامات أخرى. سيتم النظر إلى أي علامات في <title>
على أنها نص حتى تواجه علامة إغلاق لنفسها </title>
.
لماذا يتم ذلك؟ بعد كل شيء ، يمكنك فقط إخبار الرمز المميز أنه إذا استوفينا <title>
فإننا ننتقل إلى "المسار الذي نحتاجه". وهذا سيكون صحيحًا إذا لم يكن مساحات الأسماء! نعم ، تؤثر مساحة الاسم على سلوك مرحلة بناء الشجرة ، والذي بدوره يغير سلوك الرمز المميز.
كمثال ، ضع في الاعتبار سلوك علامة <title>
في مساحات أسماء HTML و SVG:
HTML
<title><span></span></title>
نتيجة بناء شجرة:
<title> "<span></span>"
Svg
<svg><title><span></span></title></svg>
نتيجة بناء شجرة:
<svg> <title> <span> ""
نرى أنه في الحالة الأولى (مساحة اسم HTML) ، تكون <span>
نصًا ، ولم يتم إنشاء عنصر span
. في الحالة الثانية (مساحة الاسم SVG) ، تم إنشاء عنصر بناءً على علامة <span>
. أي ، اعتمادًا على مساحة الاسم ، تتصرف العلامات بشكل مختلف.
لكن هذا ليس كل شيء. إن كعكة "احتفال الحياة" هذه هي حقيقة أن الرمز المميز نفسه يجب أن يعرف في أي مساحة اسم تقع مرحلة بناء الشجرة. وهذا ضروري فقط للتعامل مع CDATA
بشكل صحيح.
خذ بعين الاعتبار مثالين مع CDATA
، مساحات أسماء:
HTML
<div><![CDATA[ ]]></div>
نتيجة بناء شجرة:
<div> <!--[CDATA[ ]]-->
Svg
<div><svg><![CDATA[ ]]></svg></div>
نتيجة بناء شجرة:
<div> <svg> " "
في الحالة الأولى (مساحة اسم HTML) ، أخذ الرمز CDATA
للتعليق. في الحالة الثانية ، قام الرمز المميز بتفكيك هيكل CDATA
واستقبل البيانات منه. بشكل عام ، القاعدة هي ما يلي: إذا التقينا CDATA
ليس في مساحة اسم HTML ، فإننا نقوم بتحليلها ، وإلا فإننا نعتبرها تعليقًا.
هذا هو الاتصال الضيق بين الرمز المميز وبناء الشجرة. يجب أن يعرف الرمز المميز مساحة الاسم التي تقع فيها مرحلة بناء الشجرة حاليًا ، ويمكن أن تغير مرحلة بناء الشجرة حالة الرمز المميز.
الرموز
أدناه سننظر في جميع الأنواع الستة من الرموز المميزة التي تم إنشاؤها بواسطة الرمز المميز. وتجدر الإشارة إلى أن جميع الرموز المميزة قد أعدت بيانات ، أي تمت معالجتها بالفعل و "جاهزة للاستخدام". هذا يعني أن جميع مراجع الأحرف المسماة ، مثل ©
، سيتم تحويلها إلى أحرف unicode.
DOCTYPE Token
الرمز المميز DOCTYPE له هيكله الخاص غير المشابه للعلامات الأخرى. يحتوي الرمز على:
- الاسم الأول
- معرف عام
- معرف النظام
في HTML الحديثة ، يجب أن يبدو DOCTYPE الوحيد الصالح / الصالح على النحو التالي:
<!DOCTYPE html>
سيتم اعتبار <!DOCTYPE>
جميع الأخطاء الأخرى في التحليل.
ابدأ الرمز المميز للعلامة
قد تحتوي علامة الافتتاح على:
- اسم العلامة
- سمات
- الأعلام
على سبيل المثال ،
<div key="value" />
قد تحتوي علامة الفتح على علامة self-closing
. لا تؤثر هذه العلامة على إغلاق العلامة ، ولكنها قد تسبب خطأ في التحليل للعناصر غير الفارغة .
الرمز المميز لعلامة النهاية
علامة الإغلاق. يحتوي على جميع خصائص الرمز المميز للعلامة الافتتاحية ، ولكن يحتوي على شرطة مائلة /
أمام اسم العلامة.
</div key="value" />
قد تحتوي علامة الإغلاق على علامة إغلاق self-closing
مما سيؤدي إلى حدوث خطأ في التحليل. أيضًا ، سيحدث خطأ التحليل بسبب سمات علامة الإغلاق. سيتم تحليلها بشكل صحيح ، ولكن يتم طرحها في مرحلة بناء الأشجار.
يحتوي رمز التعليق على نص التعليق بأكمله. أي أنه يتم نسخه بالكامل من الدفق إلى الرمز المميز.
مثال
رمز الحرف
ربما الرمز الأكثر إثارة للاهتمام. رمز Unicode. قد يحتوي على حرف واحد (واحد فقط).
سيتم إنشاء رمز مميز لكل حرف في HTML وإرساله إلى مرحلة بناء الشجرة. هذا مكلف للغاية.
دعونا نرى كيف يعمل.
خذ بيانات HTML:
<span> ! ®</span>
ما رأيك كم عدد الرموز التي سيتم إنشاؤها لهذا المثال؟ الجواب: 22.
فكر في قائمة الرموز المميزة التي تم إنشاؤها:
Start tag token: <span> Character token: Character token: Character token: Character token: Character token: Character token: Character token: Character token: Character token: Character token: Character token: Character token: Character token: Character token: Character token: Character token: Character token: ! Character token: Character token: End tag token: </span> End-of-file token
لا تشعر بالراحة ، أليس كذلك؟ ولكن ، بالطبع ، العديد من منشئي محللي HTML لديهم في الحقيقة رمز واحد فقط أثناء المعالجة. تشغيله في دائرة واستبداله ببيانات جديدة في كل مرة.
دعونا نمضي قدما ونجيب على السؤال: لماذا يتم ذلك؟ لماذا لا تأخذ هذا النص في قطعة واحدة؟ الجواب يكمن في مرحلة بناء الشجرة.
الرمز المميز عديم الفائدة بدون مرحلة بناء شجرة HTML. في مرحلة بناء شجرة يتم لصق النص مع شروط مختلفة.
الشروط هي تقريبًا ما يلي:
- إذا وصل رمز مميز مع
U+0000
( NULL
) ، فإننا نتسبب في خطأ في التحليل ونتجاهل الرمز المميز. - في حالة ظهور أحد
CHARACTER TABULATION
U+0009
( CHARACTER TABULATION
) أو U+000A
( LINE FEED (LF)
) أو U+000C
( FORM FEED (FF)
) أو U+0020
( SPACE
) ، قم باستدعاء الخوارزمية لاستعادة عناصر التنسيق النشطة و أدخل الرمز المميز في الشجرة.
تتم إضافة الرمز المميز للشجرة وفقًا للخوارزمية:
- إذا لم يكن موضع الإدخال الحالي عقدة نصية ، فقم بإنشاء عقدة نصية وأدخلها في الشجرة وأضف البيانات من الرمز المميز إليها.
- بخلاف ذلك ، أضف بيانات من الرمز المميز إلى عقدة نصية موجودة.
يخلق هذا السلوك الكثير من المشاكل. الحاجة لكل رمز لإنشاء رمز مميز وإرساله للتحليل إلى مرحلة بناء شجرة. نحن لا نعرف حجم عقدة النص وعلينا إما تخصيص الكثير من الذاكرة مسبقًا أو إنشاء realoks. كل هذا مكلف للغاية من الذاكرة أو الوقت.
الرمز المميز لنهاية الملف
رمز بسيط وواضح. انتهت البيانات - دعنا نخبرك عن هذه المرحلة من بناء الأشجار.
بناء شجرة
بناء الأشجار عبارة عن جهاز حالة يحتوي على 23
ولاية مع العديد من الشروط للرموز (العلامات والنص). مرحلة بناء الشجرة هي الأكبر ، وتحتل جزءًا كبيرًا من المواصفات ، وهي أيضًا قادرة على التسبب في النوم الخفيف والتهيج.
يتم ترتيب كل شيء ببساطة شديدة. يتم استلام الرموز عند الإدخال ، وتبعاً للرمز ، يتم تبديل حالة بناء الشجرة. عند الإخراج ، لدينا DOM حقيقي.
المشاكل؟
تبدو المشاكل التالية واضحة تمامًا:
النسخ حرفًا بحرف
تتلقى كل حالة رمز مميز حرفًا واحدًا عند الإدخال ، والذي تقوم بنسخه / تحويله عند الضرورة: أسماء العلامات والسمات والتعليقات والرموز.
هذا مضيعة للغاية في الذاكرة والوقت. نحن مضطرون إلى تحديد مساحة غير معروفة من الذاكرة لكل سمة واسم العلامة والتعليق وما إلى ذلك. وهذا يؤدي ، وفقًا لذلك ، إلى realoks ، ويؤدي realoks إلى ضياع الوقت.
وإذا كنت تتخيل أن HTML يحتوي على 1000 علامة ، ولكل علامة سمة واحدة على الأقل ، فإننا نحصل على محلل بطيء بشكل هائل.
رمز الحرف
المشكلة الثانية هي رمز الحرف. اتضح أننا نقوم بإنشاء رمز مميز لكل رمز ونعطيه لبناء شجرة. لا يعرف بناء شجرة عدد الرموز المميزة التي سنحصل عليها ولا يمكننا تخصيص ذاكرة على الفور للعدد المطلوب من الأحرف. وفقًا لذلك ، هنا جميع نفس realoks + عمليات التحقق المستمر لوجود عقدة نصية في الحالة الحالية للشجرة.
نظام متآلف
المشكلة الكبرى هي اعتماد كل شيء على كل شيء. أي أن الرمز المميز يعتمد على حالة بناء الشجرة ، ويمكن أن يتحكم بناء الشجرة في الرمز المميز. وكل شيء هو المسؤول عن مساحة الاسم (مساحات الاسم).
كيف سنحل المشاكل؟
بعد ذلك ، سأصف تنفيذ محلل HTML في مشروع Lexbor الخاص بي ، بالإضافة إلى حل جميع المشاكل التي تم التعبير عنها.
المعالجة
نزيل معالجة البيانات الأولية. سوف نقوم بتدريب الرمز المميز لفهم حرف الإرجاع ( \r
) كحرف مسافة. وهكذا ، سيتم طرحه في مرحلة بناء شجرة حيث سنكتشفها.
الرموز
من خلال نقرة من المعصم ، نقوم بتوحيد جميع الرموز. سيكون لدينا رمز واحد لكل شيء. بشكل عام ، سيكون هناك رمز واحد فقط في عملية التحليل بأكملها.
سيحتوي رمزنا الموحد على الحقول التالية:
- معرف العلامة
- تبدأ
- انتهى
- السمات
- الأعلام
معرف العلامة
لن نعمل مع التمثيل النصي لاسم العلامة. نترجم كل شيء إلى أرقام. الأرقام سهلة المقارنة ، أسهل للعمل معها.
نقوم بإنشاء جدول تجزئة ثابت من جميع العلامات المعروفة. نقوم بإنشاء التعداد من جميع العلامات المعروفة. أي أننا نحتاج إلى تعيين معرّف صارم لكل علامة. وفقًا لذلك ، في جدول التجزئة ، يكون المفتاح هو اسم العلامة ، ويتم كتابة القيمة من التعداد.
على سبيل المثال:
typedef enum { LXB_TAG__UNDEF = 0x0000, LXB_TAG__END_OF_FILE = 0x0001, LXB_TAG__TEXT = 0x0002, LXB_TAG__DOCUMENT = 0x0003, LXB_TAG__EM_COMMENT = 0x0004, LXB_TAG__EM_DOCTYPE = 0x0005, LXB_TAG_A = 0x0006, LXB_TAG_ABBR = 0x0007, LXB_TAG_ACRONYM = 0x0008, LXB_TAG_ADDRESS = 0x0009, LXB_TAG_ALTGLYPH = 0x000a, }
كما ترى من المثال ، أنشأنا علامات لرمز END-OF-FILE للنص لمستند. كل هذا من أجل المزيد من الراحة. عند فتح الستارة ، سأقول أنه في العقدة ( DOM Node Interface
) سيكون لدينا Tag ID
. يتم ذلك من أجل عدم إجراء مقارنتين: على نوع العقدة والعنصر. بمعنى ، إذا كنا بحاجة إلى عنصر DIV
، فإننا نقوم بإجراء فحص واحد في العقدة:
if (node->tag_id == LXB_TAG_DIV) { }
ولكن ، بالطبع ، يمكنك القيام بذلك:
if (node->type == LXB_DOM_NODE_TYPE_ELEMENT && node->tag_id == LXB_TAG_DIV) { }
هناك حاجة إلى LXB_TAG__
في LXB_TAG__
لفصل العلامات الشائعة عن علامات النظام. بمعنى آخر ، يمكن للمستخدم إنشاء علامة باسم text
أو end-of-file
وإذا بحثنا بعد ذلك عن طريق اسم العلامة ، فلن تحدث أي أخطاء. تبدأ جميع علامات النظام بالرمز #
.
ولكن مع ذلك ، يمكن للعقدة تخزين تمثيل نصي لاسم العلامة. بالنسبة لعقد 98.99999٪ ، ستكون هذه المعلمة NULL
. في بعض مساحات الأسماء ، نحتاج إلى تحديد بادئة أو اسم علامة مع تسجيل ثابت. على سبيل المثال ، baseProfile
في مساحة الاسم SVG.
منطق العمل بسيط. إذا كان لدينا علامة مع سجل محدد بوضوح ، فعندئذٍ:
- أضفه إلى القاعدة العامة للعلامات بالأحرف الصغيرة. احصل على معرف العلامة.
- أضف معرف العلامة واسم العلامة الأصلية في تمثيل النص إلى العقدة.
العلامات المخصصة
يمكن للمطور إنشاء أي علامات في HTML. نظرًا لأن لدينا فقط تلك العلامات التي نعرفها في جدول التجزئة الثابت ، ويمكن للمستخدم إنشاء أي علامات ، نحتاج إلى جدول تجزئة ديناميكي.
كل شيء يبدو بسيطا للغاية. عندما تأتي العلامة إلينا ، سنرى ما إذا كانت موجودة في جدول التجزئة الثابت. إذا لم تكن هناك علامة ، فلنلقي نظرة على العلامة الديناميكية ، إذا لم تكن هناك علامة ، فإننا نزيد من عداد المعرف بواحد ونضيف العلامة إلى الجدول الديناميكي.
كل شيء موضح يحدث في مرحلة الرمز المميز. داخل الرمز المميز وبعد كل المقارنات ، يتم استخدام Tag ID
(مع استثناءات نادرة).
ابدأ وانتهى
الآن في رمز مميز لن يكون لدينا معالجة البيانات. لن نقوم بنسخ وتحويل أي شيء. نأخذ فقط المؤشرات إلى بداية ونهاية البيانات.
ستتم جميع عمليات معالجة البيانات ، مثل الروابط الرمزية ، في مرحلة بناء الأشجار.
وبالتالي ، سنعرف حجم البيانات للتخصيص اللاحق للذاكرة.
السمات
كل شيء بسيط هنا. نحن لا ننسخ أي شيء ، ولكننا ببساطة نحفظ المؤشرات في بداية / نهاية الاسم وقيم السمات. تحدث جميع التحولات في وقت بناء الشجرة.
الأعلام
نظرًا لأن لدينا رموزًا موحدة ، فإننا بحاجة إلى إبلاغ مبنى الشجرة بطريقة أو بأخرى عن نوع الرمز المميز. للقيام بذلك ، استخدم حقل الصورة النقطية Flags.
قد يحتوي الحقل على القيم التالية:
enum { LXB_HTML_TOKEN_TYPE_OPEN = 0x0000, LXB_HTML_TOKEN_TYPE_CLOSE = 0x0001, LXB_HTML_TOKEN_TYPE_CLOSE_SELF = 0x0002, LXB_HTML_TOKEN_TYPE_TEXT = 0x0004, LXB_HTML_TOKEN_TYPE_DATA = 0x0008, LXB_HTML_TOKEN_TYPE_RCDATA = 0x0010, LXB_HTML_TOKEN_TYPE_CDATA = 0x0020, LXB_HTML_TOKEN_TYPE_NULL = 0x0040, LXB_HTML_TOKEN_TYPE_FORCE_QUIRKS = 0x0080, LXB_HTML_TOKEN_TYPE_DONE = 0x0100 };
بالإضافة إلى نوع الرمز المميز الذي يفتح أو يغلق ، هناك قيم لمحول البيانات. يعرف الرمز المميز فقط كيفية تحويل البيانات بشكل صحيح. وفقًا لذلك ، يشير الرمز المميز في الرمز المميز إلى كيفية معالجة البيانات.
رمز الحرف
من الموصوف سابقًا ، يمكننا أن نستنتج أن الرمز المميز قد اختفى منا. نعم ، لدينا الآن نوع جديد من الرمز المميز: LXB_HTML_TOKEN_TYPE_TEXT
. نقوم الآن بإنشاء رمز مميز للنص بأكمله بين العلامات ، مع وضع علامة على كيفية معالجته في المستقبل.
وبسبب هذا ، سيتعين علينا تغيير الظروف في بناء الشجرة. نحن بحاجة إلى تدريبه على العمل ليس مع الرموز الرمزية ، ولكن مع الرموز النصية: تحويل ، حذف الأحرف غير الضرورية ، تخطي المسافات وما إلى ذلك.
ولكن ، لا يوجد شيء معقد. في مرحلة بناء شجرة ، ستكون التغييرات ضئيلة. لكن الرمز المميز لا يتطابق الآن مع المواصفات من الكلمة على الإطلاق. لكننا لسنا بحاجة إليه ، هذا طبيعي. مهمتنا هي الحصول على شجرة HTML / DOM تتوافق تمامًا مع المواصفات.
مراحل الرمز المميز
لضمان معالجة البيانات بسرعة عالية في الرمز المميز ، سنضيف مكررنا لكل مرحلة. وفقًا للمواصفات ، تقبل كل مرحلة رمزًا واحدًا لنا ، وتتخذ قرارات بناءً على الرمز الذي وصل. لكن الحقيقة هي أنها باهظة الثمن.
على سبيل المثال ، للانتقال من مرحلة ATTRIBUTE_NAME
إلى مرحلة ATTRIBUTE_VALUE
نحتاج إلى إيجاد مسافة بيضاء في اسم السمة ، والتي ستشير إلى نهايتها. وفقًا للمواصفات ، يجب أن نطعم حرفًا إلى المرحلة ATTRIBUTE_NAME
حتى ATTRIBUTE_NAME
حرف مسافة بيضاء ، ولا تتحول هذه المرحلة إلى مرحلة أخرى. هذا أمر مكلف للغاية ، وعادة ما يتم تنفيذه من خلال استدعاء دالة لكل حرف أو رد اتصال مثل "tkz-> next_code_point ()".
نضيف حلقة إلى مرحلة ATTRIBUTE_NAME
المخزن المؤقت الوارد بالكامل. في الحلقة ، نبحث عن الرموز التي نحتاج إلى تبديلها ومواصلة العمل في المرحلة التالية. هنا نحصل على الكثير من المكاسب ، حتى تحسينات المترجم.
لكن! أسوأ شيء هو أننا كسرنا بذلك دعم القطع (القطع) من الصندوق. بفضل معالجة الحرف برمز في كل مرحلة من مراحل الرمز المميز ، كان لدينا دعم للقطع ، والآن قمنا بكسرها.
كيفية إصلاحها؟ كيفية تنفيذ الدعم للقطع ؟! إنه بسيط ، نقدم مفهوم المخازن المؤقتة الواردة (المخزن المؤقت الوارد).
عازلة واردة
غالبًا ما يتم تحليل HTML في أجزاء. على سبيل المثال ، إذا تلقينا بيانات عبر الشبكة. لكي لا تقف مكتوف الأيدي أثناء انتظار البيانات المتبقية ، يمكننا إرسال البيانات المستلمة بالفعل للمعالجة / التحليل. بطبيعة الحال ، يمكن أن يتمزق البيانات في أي مكان. على سبيل المثال ، لدينا مخزنين:
أولاً
<div clas
الثانية
s="oh-no-oh-no">
نظرًا لأننا لا ننسخ أي شيء في مرحلة الرمز المميز ، ولكننا نأخذ فقط المؤشرات إلى بداية ونهاية البيانات ، لدينا مشكلة. يشير إلى مخازن المستخدمين المختلفة. وبالنظر إلى حقيقة أن المطورين غالبًا ما يستخدمون نفس المخزن المؤقت للبيانات ، فإننا نتعامل مع مؤشر إلى بداية البيانات غير الموجودة.
.
:
- (Incoming Buffer).
- ( ) , ? , . , . 99% .
" " . .
, . , ( ) . . , , , . .
:
, . , : . . ( ), . .
: . , .
.
, . , .
:
tree_build_in_body_character(token) { if (token.code_point == '\0') { } else if (token.code_point == whitespaces) { ' } /* ... */ }
Lexbor HTML
tree_build_in_body_character(token) { lexbor_str_t str = {0}; lxb_html_parser_char_t pc = {0}; pc.drop_null = true; tree->status = lxb_html_token_parse_data(token, &pc, &str, tree->document->mem->text); if (token->type & LXB_HTML_TOKEN_TYPE_NULL) { } }
, . :
pc.replace_null pc.drop_null pc.is_attribute pc.state
. - \0
, - REPLACEMENT CHARACTER
. - , - . .
, . . , <head>
. , , : " ". , .
<sarcasm>HTML ( ) sarcasm
. .
An end tag whose tag name is "sarcasm" Take a deep breath, then act as described in the "any other end tag" entry below.
.
HTML DOM/HTML Interfaces HTML/DOM HTML .
, :
- ( )
- Incoming Buffer
- Tag ID
- ̆ : , N+
- ̆
- ̈
i7 2012 , , 235MB (Amazon-).
, 1.5/2 , . , . , CSS (Grammar, , Grammar). HTML, CSS , "".
كود المصدر
HTML Lexbor HTML .
PS
CSS Grammar. , . - 6-8 .
شكرا لاهتمامكم!