الميزات القادمة للتعبير العادي

قدّم ES2015 العديد من الميزات الجديدة في لغة JavaScript، بما في ذلك تحسينات كبيرة على بنية التعبير العادي باستخدام علامتَي Unicode (/u) وsticky (/y). ولم يتوقف التطوير منذ ذلك الحين. في إطار التعاون الوثيق مع أعضاء آخرين في TC39 (هيئة معايير ECMAScript)، ساهم فريق V8 في اقتراح العديد من الميزات الجديدة وتصميمها معًا لجعل التعبيرات العادية أكثر فعالية.

يتم حاليًا اقتراح تضمين هذه الميزات في مواصفات JavaScript. على الرغم من أنّ الاقتراحات لم يتم قبولها بالكامل، فهي في المرحلة 3 من عملية TC39. لقد نفّذنا هذه الميزات من خلال علامة (راجِع المعلومات أدناه) لنتمكّن من تقديم ملاحظات بشأن التصميم و التنفيذ في الوقت المناسب لمؤلفي الاقتراحات المعنيّين قبل اكتمال المواصفات.

تقدّم لك مشاركة المدونة هذه معاينة لهذا المستقبل المثير. إذا أردت متابعة المثالَين التاليَين، فعِّل ميزات JavaScript التجريبية على chrome://flags/#enable-javascript-harmony.

عمليات الالتقاط المُسمّاة

يمكن أن تحتوي التعبيرات العادية على ما يُعرف باسم عمليات الالتقاط (أو المجموعات)، والتي يمكنها التقاط جزء من النص المطابق. حتى الآن، لم يكن بإمكان المطوّرين الإشارة إلى عمليات الالتقاط هذه إلا من خلال فهرسها الذي يتم تحديده حسب موضع عملية الالتقاط ضمن النمط.

const pattern = /(\d{4})-(\d{2})-(\d{2})/u;
const result = pattern.exec('2017-07-10');
// result[0] === '2017-07-10'
// result[1] === '2017'
// result[2] === '07'
// result[3] === '10'

ولكن من المعروف أنّه من الصعب قراءة التعبيرات العادية وكتابتها و الحفاظ عليها، ويمكن أن تضيف المراجع الرقمية المزيد من التعقيدات. على سبيل المثال، في الأنماط الأطول، قد يكون من الصعب تحديد فهرس عملية تسجيل معيّنة:

/(?:(.)(.(?<=[^(])(.)))/  // Index of the last capture?

والأسوأ من ذلك، يمكن أن تؤدي التغييرات في أحد الأنماط إلى تغيير فهارس كل عمليات الالتقاط المتوفّرة:

/(a)(b)(c)\3\2\1/     // A few simple numbered backreferences.
/(.)(a)(b)(c)\4\3\2/  // All need to be updated.

إنّ عمليات الالتقاط المُسمّاة هي ميزة قادمة تساعد في التخفيف من هذه المشاكل من خلال السماح للمطوّرين بتعيين أسماء لعمليات الالتقاط. تشبه بنية الجملة بنية Perl وجافا وNet. وRuby:

const pattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
const result = pattern.exec('2017-07-10');
// result.groups.year === '2017'
// result.groups.month === '07'
// result.groups.day === '10'

يمكن أيضًا الإشارة إلى عمليات الالتقاط المُعنوَنة من خلال المراجعات الخلفية المُعنوَنة وعبر String.prototype.replace:

// Named backreferences.
/(?<LowerCaseX>x)y\k<LowerCaseX>/.test('xyx');  // true

// String replacement.
const pattern = /(?<fst>a)(?<snd>b)/;
'ab'.replace(pattern, '$<snd>$<fst>');                              // 'ba'
'ab'.replace(pattern, (m, p1, p2, o, s, {fst, snd}) => fst + snd);  // 'ba'

تتوفّر التفاصيل الكاملة لهذه الميزة الجديدة في اقتراح المواصفات.

علامة dotAll

يتطابق رمز . تلقائيًا في التعبيرات العادية مع أي حرف باستثناء علامات إنهاء السطر:

/foo.bar/u.test('foo\nbar');   // false

يقدّم الاقتراح وضع dotAll، الذي يتم تفعيله من خلال العلامة /s. في وضع dotAll ، يتطابق الرمز . مع عناصر إنهاء السطر أيضًا.

/foo.bar/su.test('foo\nbar');  // true

تتوفّر التفاصيل الكاملة لهذه الميزة الجديدة في اقتراح المواصفات.

ترميزات سمات Unicode

مع إدخال تنسيق Unicode في ES2015، أصبح هناك فجأة العديد من الأحرف التي يمكن اعتبارها أرقامًا، مثل الرقم واحد المُحاط بدائرة: ①، أو أحرف كلمات، مثل الحرف الصيني للثلج: 雪.

ولا يمكن مطابقة أيّ منهما مع \d أو \w. سيؤدي تغيير معنى هذه الاختصارات إلى إيقاف أنماط التعبيرات العادية الحالية.

بدلاً من ذلك، يتم طرح تسلسلات جديدة لعلامات الهروب الخاصة بالمواقع. تجدر الإشارة إلى أنّها لا تتوفّر إلا للتعبيرات العادية المتوافقة مع Unicode والتي يُشار إليها باستخدام العلامة /u.

/\p{Number}/u.test('①');      // true
/\p{Alphabetic}/u.test('雪');  // true

يمكن مطابقة القيمة المقلوبة باستخدام \P.

/\P{Number}/u.test('①');      // false
/\P{Alphabetic}/u.test('雪');  // false

يحدّد اتحاد Unicode العديد من السمات الأخرى، مثل رموز الرياضيات أو أحرف الهيراغانا اليابانية:

/^\p{Math}+$/u.test('∛∞∉');                            // true
/^\p{Script_Extensions=Hiragana}+$/u.test('ひらがな');  // true

يمكن العثور على القائمة الكاملة لفئات سمات Unicode المتوافقة في اقتراح المواصفات الحالي. لمزيد من الأمثلة، يمكنك الاطّلاع على هذه المقالة الإرشادية.

التأكيدات التي تبحث للخلف

كانت التأكيدات على النظر إلى الأمام جزءًا من بنية التعبير العادي في JavaScript منذ البداية. أخيرًا، تم إدخال نظير هذه العبارة، وهو عبارة البحث إلى الخلف. قد يتذكر بعضكم أنّ هذه الميزة كانت جزءًا من الإصدار 8 منذ فترة طويلة. ونحن نستخدم أيضًا عمليات التحقّق من المحتوى السابق في الخلفية لتنفيذ علامة Unicode المحدّدة في ES2015.

يصف الاسم معناه بشكل جيد. ويقدّم هذا النمط طريقة لتقييد تطابق نمط معيّن فقط إذا سبقه النمط في مجموعة البحث إلى الخلف. ويتوفر بطعمين متطابقَين وغير متطابقَين:

/(?<=\$)\d+/.exec('$1 is worth about ¥123');  // ['1']
/(?<!\$)\d+/.exec('$1 is worth about ¥123');  // ['123']

لمزيد من التفاصيل، يمكنك الاطّلاع على مشاركة المدوّنة السابقة التي تهدف إلى توضيح التأكيدات التي تبحث للخلف، والأمثلة في حالات اختبار V8 ذات الصلة.

الشكر والتقدير

لن تكتمل مشاركة المدونة هذه بدون ذكر بعض الأشخاص الذين عملوا بجد لتحقيق ذلك، خاصةً خبراء اللغة ماتياس بينينز ودان إهرنبرج وكلود باتشي وبرايان تيرلسون وتوماس وود وغوركيم ياكين، بالإضافة إلى خبير Irregexp إريك كورّي، بالإضافة إلى جميع الأشخاص الآخرين الذين ساهموا في مواصفات اللغة وتنفيذ V8 لهذه الميزات.

نأمل أن تعجبك هذه الميزات الجديدة للتعبيرات العادية مثلنا.