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

יאקוב גרובר
יאנג גואו

ES2015 כללו תכונות חדשות רבות בשפת ה-JavaScript, כולל שיפורים משמעותיים בתחביר של הביטויים הרגולריים עם הדגלים Unicode (/u) ובדגלים במיקום קבוע (/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'

אפשר להפנות לצילומי וידאו בעלי שם גם באמצעות קובצי עזר חוזרים בעלי שם ודרך 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'

פרטים מלאים של תכונה חדשה זו זמינים בהצעת המפרט.

דגל נקודהכל

כברירת מחדל, האטום . בביטויים רגולריים תואם לכל תו פרט למסיים שורה:

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

הצעה מציגה את מצב dotAll, המופעל באמצעות הדגל /s. במצב dotAll, . תואם גם לסימני פיסוק.

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

פרטים מלאים של תכונה חדשה זו זמינים בהצעת המפרט.

תווי escape של מאפיין Unicode

עם כניסת ה-Unicode ל-ES2015, יש פתאום הרבה תווים נוספים שיכולים להיחשב כמספרים – למשל הספרה המוקפת בעיגול אחת: 1; או לתווי מילים כמו 'שלג': 雪.

אי אפשר להתאים לאף אחת מהאפשרויות האלה ל-\d או ל-\w. שינוי המשמעות של הקיצורים האלה יגרום לשבירת הדפוסים הקיימים של ביטויים רגולריים.

במקום זאת, מוצגים רצפי Escape חדשים של מאפיין. שימו לב שהם זמינים רק לביטויים רגולריים בעלי מודעות ל-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 מלכתחילה. סוף סוף הציגו את המקביל שלהם, שמסתכלים מאחורי הטענות האלה. יכול להיות שחלק מכם עדיין יזכרו שהמשחק הזה הוא חלק מ-V8 לא מעט זמן. אנחנו אפילו משתמשים בטענות אימות מאחורי הקלעים כדי להטמיע את דגל ה-Unicode שצוין ב-ES2015.

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

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

לפרטים נוספים, קראו את הפוסט הקודם בבלוג שעוסק בטענות נכונות, ובדוגמאות במקרי בדיקה של V8.

אישורים

פוסט זה בבלוג לא יהיה מושלם אם לא יצוינו כמה מהאנשים שעבדו קשה כדי להשיג את המטרה הזו: אלופים במיוחד את השפה מתיאס ביינס, דן ארנברג, קלוד פאצ'ה, בריאן טרלסון, תומאס ווד, Gorkem Yakin ו-Irregexp guru Erik Corry סיפק גם הוא את התכונות האלה, אך נוסף לכל אחד,

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