תכונות עתידיות של ביטויים רגולריים

בגרסה 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,‏ Java,‏ ‎.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'

אפשר גם להפנות לקטעי נתונים שהוגדרו בשם באמצעות הפניות לאחור (backreference) שהוגדרו בשם, ודרך 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 מופיעה בהצעה הנוכחית למפרט. דוגמאות נוספות זמינות במאמר הזה.

טענות נכוֹנוּת (assertions) לאחור

טענות נכוֹנוּת לבדיקה מראש היו חלק מתחביר הביטויים הרגולריים של JavaScript כבר מההתחלה. סוף סוף אנחנו משיקים את המקבילה שלהן, טענות נכוֹנוּת (assertions) לאחור. יכול להיות שחלק מכם זוכרים שהתכונה הזו כבר הייתה חלק מ-V8 במשך זמן מה. אנחנו אפילו משתמשים בטענות נכוֹנוּת (assert) של בדיקה לאחור כדי להטמיע את הדגל של Unicode שצוין ב-ES2015.

השם כבר מתאר את המשמעות שלו בצורה טובה למדי. היא מאפשרת להגביל את ההתאמה של דפוס רק אם הוא מופיע אחרי הדפוס בקבוצת ה-lookbehind. יש לו שתי גרסאות: תואמת ולא תואמת:

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

פרטים נוספים זמינים בפוסט הקודם שלנו בבלוג, שמתמקד בהצהרות נכוֹנוּת לאחור, וכן בתרחישים לדוגמה של בדיקות V8.

תודות

פוסט הבלוג הזה לא יהיה שלם בלי להזכיר כמה מהאנשים שעבדו קשה כדי שזה יקרה: במיוחד מומחי השפה Mathias Bynens,‏ Dan Ehrenberg,‏ Claude Pache,‏ Brian Terlson,‏ Thomas Wood,‏ Gorkem Yakin ו-Erik Corry, מומחה Irregexp, וגם כל מי שתרם למפרט השפה ולהטמעה של התכונות האלה ב-V8.

אנחנו מקווים שהתכונות החדשות של ביטויים רגולריים יגרמו לכם להתרגש כמונו.