جافا سكريبت ، بدون بعض النماذج المعيارية المخصصة ، غير قادرة على التعامل بشكل صحيح مع أحرف Unicode / نقاط التشفير خارج BMP ، أي تلك التي يتطلب تشفيرها أكثر من 16 بت.
يبدو أن هذا القيد ينتقل إلى PEG.js ، كما هو موضح في المثال أدناه.
على وجه الخصوص ، أود أن أكون قادرًا على تحديد نطاقات مثل [\u1D400-\u1D419]
(والذي يتحول حاليًا إلى [ᵀ0-ᵁ9]
) أو ما يعادله [𝐀-𝐙]
(والذي يطرح "نطاق أحرف غير صالح" خطأ). (واستخدام تدوين ES6 الجديد [\u{1D400}-\u{1D419}]
يؤدي إلى الخطأ التالي: SyntaxError: Expected "!", "$", "&", "(", ".", character class, comment, end of line, identifier, literal, or whitespace but "[" found.
.)
هل توجد طريقة لإجراء هذا العمل لا يتطلب تغييرات في PEG.js؟
رمز المثال:
هذه القواعد:
//MathUpper = [𝐀-𝐙]+
MathUpperEscaped = [\u1D400-\u1D419]+
سلوك متوقع:
المحلل اللغوي الذي تم إنشاؤه من القواعد النحوية المحددة يوزع بنجاح ، على سبيل المثال ، "𝐀𝐁𝐂".
السلوك الفعلي:
خطأ في التحليل: Line 1, column 1: Expected [ᵀ0-ᵁ9] but "
(أو عند إلغاء التعليق على القاعدة الأخرى ، يظهر خطأ "نطاق الأحرف غير صالح".)
لأكون صادقًا تمامًا ، بصرف النظر عن تحديث دعم Unicode ومثال JavaScript ، لدي القليل من المعرفة أو لا أعرف شيئًا عن Unicode ، لذلك لا يمكنني حاليًا حل هذه المشكلة (تم ذكرها بوضوح في كل من القواعد النحوية: _Non يتم تجاهل أحرف -BMP تمامًا_).
في الوقت الحالي ، أثناء العمل على مشاريع شخصية ومشاريع متعلقة بالعمل أكثر إلحاحًا (بما في ذلك _PEG.js 0.x_) ، سأستمر في انتظار شخص يفهم Unicode بشكل أفضل لتقديم بعض العلاقات العامة 😆 ، أو في النهاية الالتفاف عليه بعد _PEG. js v1_ ، آسف صديقي.
لمعلوماتك ، يبدو أن الأزواج البديلة تعمل. قواعد اللغة
start = result:[\uD83D\uDCA9]+ {return result.join('')}
يوزع 💩 وهو u + 1F4A9. لاحظ أن نتيجة الانضمام ('') تضع الزوج البديل معًا مرة أخرى ، وإلا ستحصل على ['\uD83D','\uDCA9']
بدلاً من hankey. النطاقات ستكون مشكلة.
المزيد عن الأزواج البديلة: https://en.wikipedia.org/wiki/UTF-16#U + 010000_to_U + 10FFFF
هذا بأي حال من الأحوال يحل محل ما طلب OP.
drewnolan شكرا على الرؤساء 👍
لسوء الحظ ، تحلل القواعد النحوية أيضًا \uD83D\uD83D
.
بالنسبة للآخرين الذين واجهوا هذه المشكلة: أنا محظوظ لأنني بحاجة فقط إلى التعامل مع مجموعة فرعية صغيرة من نقاط التشفير خارج BMP ، لذلك انتهى بي الأمر بتعيينهم في منطقة الاستخدام الخاص لـ BMP قبل تحليل وعكس هذا التعيين مباشرة بعد ذلك .
هذا الحل محفوف بالمشاكل بشكل واضح في الحالة العامة ، لكنه يعمل بشكل جيد في مجال مشكلتي.
futagoza - سأبذل قصارى جهدي للتوضيح. أنت تواجه العديد من المشاكل هنا.
utf-16
، ucs4
، إلخ. هذه هي الطريقة التي يتم بها ترميز codepoints
، وهي البيانات المقصودة ، على هيئة بايت. يتيح لك utf-16-le
حسب المثال ترميز معظم الأحرف كأزواج ثنائية البايت تسمى code units
، لكن استخدم مجموعات من وحدات الكود للتعبير عن أحرف عالية القيمة تصل إلى 0x10ffff
.أريد هذا أيضًا ، لكن من الناحية الواقعية ، لن يحدث هذا
القرف المقدس ، قام شخص ما باستبدال محلل سلسلة كامل منذ ما يقرب من عام ، وقد تعرفوا على النفقات العامة ، لذلك سمحوا لنا باستخدام سلاسل JS القياسية عادةً
لماذا لم يتم الاندماج
تضمين التغريدة ولكن لماذا تواجه المشرف الحالي وقتًا عصيبًا؟ لا أحد يدين بأي شيء. لماذا لا تحتفظ بالشوكة الخاصة بك؟
لا يوجد مشرف حالي. الشخص الذي تولى PEG لم يفرج عن أي شيء. لقد عمل على القاصر التالي لمدة ثلاث سنوات ، ثم قال إنه لا يعجبه شكله ، فهو يرمي كل peg.js
بعيدًا ، ويبدأ من جديد من شيء كتبه من الصفر بلغة مختلفة ، مع كلمة غير متوافقة أست.
فقدت الأداة نصف قاعدة مستخدميها في انتظار ثلاث سنوات على هذا الرجل لارتكاب إصلاحات من سطر واحد كتبها أشخاص آخرون ، مثل دعم الوحدة النمطية es6 ، ودعم الكتابة على الحروف ، ودعم الأسهم ، و unicode الممتد ، وما إلى ذلك.
هناك عشرات الأشخاص يطلبون منه الاندماج ويظل يقول "لا ، هذا هو مشروعي للهواية الآن ولا يعجبني ما هو عليه"
الكثير من الأشخاص لديهم شركات قائمة على هذا المحلل اللغوي. إنهم مشدودون تمامًا.
وعد هذا الرجل بأن يكون مشرفًا على أداة بالغة الأهمية ، ولم يقم بأي صيانة. حان الوقت للسماح لشخص آخر بالحفاظ على هذه المكتبة في وضع جيد الآن.
لماذا لا تحتفظ بالشوكة الخاصة بك؟
لدي لمدة ثلاث سنوات حتى الآن. تم إصلاح ما يقرب من ثلث أداة تعقب المشكلات في peg
.
اضطررت إلى استنساخها وإعادة تسميتها وعمل شوكة جديدة لإصلاح مشكلة الحجم لمحاولة الالتزام بها ، لأنني قد انجرفت كثيرًا
حان الوقت لكي يتلقى أي شخص آخر هذه الإصلاحات ، بالإضافة إلى الإصلاحات التي كانت موجودة في جهاز التعقب منذ عام 2017.
هذا الرجل لا يحافظ على الوتد. تركها تموت.
حان وقت التغيير.
drewnolan - لذا ، لست متأكدًا مما إذا كان هذا مثيرًا للاهتمام أم لا ، لكن الأزواج البديلة لا تعمل في الواقع. إنهم يفعلون ذلك بالصدفة فقط.
لفهم المشكلة الأساسية ، عليك التفكير في نمط بت مستوى التشفير ، وليس مستوى التمثيل الأول.
بمعنى ، إذا كانت لديك قيمة unicode تساوي 240 ، فسيفكر معظم الناس "أوه ، إنه يعني 0b1111 0000
." لكن في الواقع ، ليس هذا هو ما يمثله Unicode 240 ؛ يتم تمثيل ما يزيد عن 127 بمقدار 2 بايت ، لأن البت العلوي عبارة عن علم ، وليس بت قيمة. لذا فإن 240 في Unicode هي في الواقع تخزين 0b0000 0001 0111 0000
(ما عدا في utf-7 ، وهو أمر حقيقي وليس خطأ مطبعي ، حيث تصبح الأشياء غريبة للغاية. ونعم ، أعلم أن ويكيبيديا تقول إنها ليست قيد الاستخدام. ويكيبيديا خاطئة . هذا هو ما يتم إرسال الرسائل القصيرة SMS عبره ؛ قد يكون ترميز الأحرف الأكثر شيوعًا من خلال إجمالي عدد الزيارات.)
لذا ها هي المشكلة.
إذا كتبت بايت STUV WXYZ ، ثم في utf16 ، من بيانات ucs4 ، إذا تم قطع الشيء الخاص بك إلى النصف ، فغالبًا ما يمكنك إعادة تثبيته معًا مرة أخرى.
مرة واحدة في 128 لا يمكنك ذلك ، بالنسبة للأحرف التي تزيد عن 2 بايت في الأصل. (يبدو أنه رقم محدد بشكل فظيع ، أليس كذلك؟)
لأنه عندما يكون هذا الجزء العلوي في موضع البايت الثاني قيد الاستخدام ، فإن قصه إلى نصفين سيضيف صفرًا حيث كان ينبغي أن يكون واحدًا. تدبيسهم معًا مرة أخرى جنبًا إلى جنب لأن البيانات الثنائية لا تزيل مفهوم القيمة مرة أخرى. القيمة التي تم فك ترميزها لا تكافئ القيمة المشفرة ، وأنت تقوم بإلحاق عمليات فك التشفير ، وليس الترميزات.
يحدث أن تكون معظم الرموز التعبيرية خارج هذا النطاق. ومع ذلك ، فإن القطع الكبيرة من عدة لغات ليست كذلك ، بما في ذلك اللغة الصينية النادرة ومعظم الرياضيات والرموز الموسيقية.
من المؤكد أن أسلوبك جيد بما يكفي لجميع الرموز التعبيرية تقريبًا ولكل لغة بشرية مشتركة بما يكفي للدخول إليها بواسطة Unicode 6 ، وهو تحسن كبير على الوضع الراهن
ولكن يجب دمج هذا العلاقات العامة ، بمجرد اختباره بشكل كافٍ للتأكد من صحته وضد مشكلات الأداء غير المتوقعة (تذكر أن مشكلات أداء unicode هي سبب وفاة php)
يبدو أن التعبير . (dot character)
يحتاج أيضًا إلى وضع Unicode. قارن:
const string = '-🐎-👱-';
const symbols = (string.match(/./gu));
console.log(JSON.stringify(symbols, null, ' '));
const pegResult = require('pegjs/')
.generate('root = .+')
.parse(string);
console.log(JSON.stringify(pegResult, null, ' '));
انتاج:
[
"-",
"🐎",
"-",
"👱",
"-"
]
[
"-",
"\ud83d",
"\udc0e",
"-",
"\ud83d",
"\udc71",
"-"
]
لقد عملت مؤخرًا على هذا ، باستخدام # 616 كأساس وتعديله لاستخدام بناء جملة ES6 \u{hhhhhhh}
، وسوف أقوم بإنشاء علاقات عامة في بعض الساعات.
يعد حساب نطاقات UTF-16 regex مقسمة إلى بدائل أمرًا معقدًا بعض الشيء واستخدمت https://github.com/mathiasbynens/regenerate لهذا الغرض ؛ ستكون هذه هي التبعية الأولى لـ pegjs الحزمة ، وآمل أن يكون ذلك ممكنًا (هناك أيضًا polyfills لخصائص Unicode التي يمكن إضافتها كاعتمادية ، انظر # 648). راجع ويكيبيديا إذا كنت لا تعرف بدائل UTF-16 .
لجعل PEG.js متوافقًا مع Unicode بأكمله ، هناك مستويات مختلفة:
.
للقبض على وحدة أو وحدتين من الكود.بالنسبة لمعظم النقاط ، يمكننا أن نكون متوافقين مع الإصدارات السابقة وأن ننشئ محللات مشابهة جدًا لتلك القديمة ، باستثناء النقطة 5 لأن نتيجة التحليل يمكن أن تعتمد على ما إذا كانت قاعدة النقطة تلتقط وحدة أو وحدتين من الكود. لهذا أقترح إضافة خيار وقت التشغيل للسماح للمستخدم بالاختيار بين خيارين أو ثلاثة خيارات:
يمكن تحليل فئة Regex بشكل ثابت أثناء إنشاء المحلل اللغوي للتحقق مما إذا كان لها طول ثابت (في عدد وحدات الكود). هناك ثلاث حالات: 1. فقط BMP أو وحدة رمز واحدة ، أو 2. وحدتا رمز فقط ، أو 3. وحدة أو وحدتان من وحدات الكود حسب وقت التشغيل. في الوقت الحالي ، يفترض الرمز الثانوي أن فئة regex هي دائمًا وحدة رمز واحدة ( انظر هنا ). باستخدام التحليل الثابت ، يمكننا تغيير هذه المعلمة لتعليمات الرمز الثانوي هذه إلى 1 أو 2 للحالتين الأوليين. لكن بالنسبة للحالة الثالثة ، أعتقد أنه يجب إضافة تعليمة بايت كود جديدة ، في وقت التشغيل ، للحصول على عدد وحدات الكود المتطابقة وزيادة المؤشر وفقًا لذلك. الخيارات الأخرى التي لا تحتوي على تعليمات بايت كود جديدة ستكون: 1. لحساب عدد وحدات الكود المطابقة دائمًا ، لكن هذه عقوبة أداء أثناء التحليل لمحللات BMP فقط ، لذلك لا أحب هذا الخيار ؛ 2. لحساب ما إذا كانت وحدة الكود الحالية عبارة عن بديل مرتفع يتبعها بديل منخفض لزيادة 1 أو 2 ، ولكن هذا من شأنه أن يفترض أن القواعد النحوية كانت دائمًا بدائل UTF-16 جيدة التكوين دون إمكانية كتابة القواعد النحوية مع بدائل وحيدة ( انظر النقطة التالية) وهذه أيضًا عقوبة أداء لمحللي BMP فقط.
هناك مسألة البدائل الوحيدة (بديل مرتفع بدون بديل منخفض بعده ، أو بديل منخفض بدون بديل مرتفع قبله). رأيي في هذا هو أن فئة regex يجب أن تكون حصرية: إما مع بدائل وحيدة إما بأحرف UTF-16 Unicode جيدة التكوين (BMP أو بديل مرتفع يتبعها بديل منخفض) ، وإلا فهناك خطر أن مؤلفي القواعد النحوية غير مدركين لها تمزج الخواص الدقيقة لـ UTF-16 معًا ولا تفهم النتيجة ، ويمكن لمؤلفي القواعد النحوية الذين يرغبون في إدارة بدائل UTF-16 أن يفعلوا ذلك باستخدام قواعد PEG لوصف الروابط بين بدائل محددة عالية ومنخفضة. أقترح إضافة زائر يطبق هذه القاعدة أثناء إنشاء المحلل اللغوي.
في الختام ، ربما يكون من الأسهل إدارة مسألة البدائل الوحيدة في PEG عنها في regex لأن محلل PEG يتقدم دائمًا ، لذلك يتم التعرف على وحدة الكود التالية إما أنها ليست كذلك ، على عكس regexes حيث من المحتمل أن يرتبط بعض التراجع أو قم بفصل مركب مرتفع مع بديل منخفض وبالتالي قم بتغيير عدد أحرف Unicode المتطابقة ، إلخ.
العلاقات العامة لـ ES6 لحرف Unicode النجمي هي # 651 بناءً على # 616 وتطوير الفصول هو https://github.com/Seb35/pegjs/commit/0d33a7a4e13b0ac7c55a9cfaadc16fc0a5dd5f0c لتنفيذ النقطتين 2 و 3 في تعليقي أعلاه ، وفقط اختراق سريع لزيادة المؤشر (النقطة 4) ولا شيء في الوقت الحالي للقاعدة النقطة .
(النقطة 5).
تم الانتهاء في الغالب من تطوري الحالي حول هذه المشكلة ، وكان العمل الأكثر تقدمًا في https://github.com/Seb35/pegjs/tree/dev-astral-classes-final. يتم التعامل مع جميع النقاط الخمس المذكورة أعلاه ويحاول السلوك العام تقليد JS regexes فيما يتعلق بحالات الحافة (وهناك الكثير منها).
السلوك العام محكوم بالخيار unicode
مشابه للعلامة unicode
في JS regexes: يتم زيادة المؤشر من حرف Unicode واحد (وحدة أو وحدتان من الكود) اعتمادًا على النص الفعلي (على سبيل المثال [^a]
يطابق النص "💯" ويزداد المؤشر بمقدار وحدتي رمز). عندما يكون الخيار unicode
خاطئًا ، يتم دائمًا زيادة المؤشر بمقدار وحدة رمز واحدة.
فيما يتعلق بالإدخال ، لست متأكدًا مما إذا كنا نصمم PEG.js بنفس طريقة JS regexes: هل يجب أن نفوض [\uD83D\uDCAD-\uD83D\uDCAF]
) في القواعد النحوية في وضع non-Unicode؟ يمكننا إحداث فرق بين "إدخال Unicode" و "إخراج Unicode":
أنا شخصياً أعتقد أنني أفضل أن نسمح بإدخال Unicode ، إما بشكل دائم أو بخيار افتراضي true
نظرًا لعدم وجود نفقات إضافية كبيرة وهذا من شأنه تمكين هذا الاحتمال للجميع افتراضيًا ، ولكن يجب أن يظل إخراج Unicode false
لأن أداء المحلل اللغوي الذي تم إنشاؤه أفضل (دائمًا ما تكون الزيادة في المؤشر 1).
فيما يتعلق بهذه المشكلة بشكل عام (وحول إخراج Unicode الافتراضي إلى false
) ، يجب أن نضع في اعتبارنا أنه من المحتمل بالفعل تشفير أحرف Unicode في قواعد النحو لدينا ، بسعر فهم أداء UTF-16 :
// rule matching [\u{1F4AD}-\u{1F4AF}]
my_class = "\uD83D" [\uDCAD-\uDCAF]
// rule matching any Unicode character
my_strict_unicode_dot_rule = $( [\u0000-\uD7FF\uE000-\uFFFF] / [\uD800-\uDBFF] [\uDC00-\uDFFF] )
// rule matching any Unicode character or a lone surrogate
my_loose_unicode_dot_rule = $( [\uD800-\uDBFF] [\uDC00-\uDFFF]? / [\u0000-\uFFFF] )
لذلك يمكن لمؤلف القواعد النحوية الذي يريد كلاً من المحلل اللغوي السريع والقدرة على التعرف على أحرف Unicode في أجزاء معينة من قواعده / قواعده استخدام هذه القاعدة. وبالتالي فإن هذه المشكلة تتعلق فقط بتبسيط إدارة Unicode دون الغوص في عناصر UTF-16 الداخلية.
حول التنفيذ ، اعتبرت في محاولتي الأولى أن نص القواعد تم ترميزه بأحرف Unicode وأن قاعدة "النقطة" لمحلل PEG.js تتعرف على أحرف Unicode. عادت المحاولة الثانية والأخيرة إلى هذا (قاعدة النقطة هي دائمًا وحدة رمز واحدة للتحليل السريع) وهناك خوارزمية صغيرة في إعداد الزائر unicode-class.js لإعادة تكوين أحرف Unicode المنقسمة في فئات الأحرف (على سبيل المثال [\uD83D\uDCAD-\uD83D\uDCAF]
تم التعرف عليه نحويًا كـ [ "\uD83D", [ "\uDCAD", "\uD83D" ], "\uDCAF" ]
وهذه الخوارزمية تحول هذا إلى [ [ "\uD83D\uDCAD", "\uD83D\uDCAF" ] ]
). كنت أتخيل أن أكتب هذا في القواعد النحوية نفسها ، لكن كان من الممكن أن يكون طويلًا ، والأهم من ذلك أن هناك طرقًا متعددة لتشفير الأحرف ("💯" ، "uD83DuDCAF" ، "u {1F4AF}") ، لذلك من الأسهل كتابتها في زائر.
لقد أضفت شفرتي تشغيل في المحاولة الثانية:
(input.charCodeAt(currPos) & 0xFC00) === 0xD800 && input.length > currPos + 1 && (input.charCodeAt(currPos+1) & 0xFC00) === 0xDC00
classes[c].test(input.substring(currPos, currPos+2)
ACCEPT_N
، ويتم تقسيم فئات الأحرف إلى قسمين regexes ذات طول ثابت (1 أو 2 وحدة رمز).لقد أجريت بعض التحسينات باستخدام ميزة "المطابقة" ، حيث ألغيت أثناء إنشاء مسارات "الشفرة الميتة" اعتمادًا على الوضع (Unicode أم لا) وفئة الأحرف.
لاحظ أيضًا أن regexes تكون دائمًا إيجابية في هذا التنفيذ: تُرجع regexes المقلوبة العكس مما يؤدي إلى الرمز الثانوي. كان هذا أسهل لتجنب حالات الحافة حول البدائل.
لقد كتبت بعض الوثائق ولكن ربما سأضيف المزيد (ربما دليل لشرح بسرعة تفاصيل خيار (خيارات) يونيكود والمقتطفات مع قاعدة نقطة يونيكود محلية الصنع). سوف أقوم بإضافة الاختبارات قبل إرسالها كعلاقات عامة.