ECMAScript | Прекращение (Term) | Регулярные выражения (Regular Expression)

ECMAScript | Прекращение (Term) | Регулярные выражения (Regular Expression)

С параметром направления — direction.

Производство Term :: Assertion оценивается следующим образом:

1. Верните совпадение (Matcher), которое является результатом оценки Утверждения Assertion.
Примечание

Результирующий Matcher не зависит от направления direction.

Производство Term :: Atom оценивается следующим образом:

1. Верните Matcher, который является результатом вычисления Атома Atom с аргументом направления direction.

 

Производственный Term :: Atom Quantifier оценивается следующим образом:

1. Оцените Atom с аргументом direction, чтобы получить Matcher m.
2. Оцените Quantifier, чтобы получить три результата: неотрицательное целое число min, неотрицательное целое число (или +∞) max и Логическое значение - greedy (жадный).
3. Утвердить: minmax.
4. Пусть parenIndex будет числом скобок с левым захватом во всем регулярном выражении, которое встречается слева от этого Term. Это общее количество узлов синтаксического  анализа Atom :: ( GroupSpecifier Disjunction ) до или до этого Term.
5. Пусть parenCount будет количеством закрывающих слева круглых скобок в Atom. Это общее количество узлов синтаксического анализа Atom :: ( GroupSpecifier Disjunction ), заключенных в Atom.
6. Верните новый Matcher с параметрами (x, c), который захватывает m, min, max, greedy, parenIndex и parenCount и при вызове выполняет следующие шаги:
   а. Утвердить: x - это состояние (State).
   b. Утвердить: c - это продолжение (Continuation).
   c. Вернуть ! RepeatMatcher(m, min, max, greedy, x, c, parenIndex, parenCount).

 

RepeatMatcher(m, min, max, greedy, x, c, parenIndex, parenCount)

Абстрактная операция RepeatMatcher (Повторить сопоставление) принимает аргументы m (Matcher), min (неотрицательное целое число), max (неотрицательное целое число или +∞), greedy (логическое значение), x (состояние), c (продолжение) , parenIndex (неотрицательное целое число) и parenCount (неотрицательное целое число). При вызове она выполняет следующие шаги:

1. Если max = 0, вернуть c(x).
2. Пусть d будет новым продолжением (Continuation) с параметрами (y), которое захватывает m, min, max, greedy, x, c, parenIndex и parenCount и при вызове выполняет следующие шаги:
   а. Утвердить: y - это состояние (State).
   b. Если min = 0 и endIndex y = endIndex x, вернуть ошибку failure.
   c. Если min = 0, пусть min2 будет 0; в противном случае пусть min2 будет min - 1.
   d. Если max равно +∞, пусть max2 равно +∞; в противном случае пусть max2 будет max - 1.
   е. Вернуть ! RepeatMatcher(m, min2, max2, greedy, y, c, parenIndex, parenCount).
3. Пусть cap будет копией Списка captures для x.
4. Для каждого целого числа k, такого что parenIndex < k и kparenIndex + parenCount, установите cap[k] в значение undefined.
5. Пусть e будет endIndex для x.
6. Пусть xr будет состоянием (e, cap).
7. Если min ≠ 0, вернуть m(xr, d).
8. Если жадность greedy ложна (является false), то
   а. Пусть z будет c(x).
   b. Если z не является ошибкой failure, верните z.
   c. Вернуть m(xr, d).
9. Пусть z равно m(xr, d).
10. Если z не является ошибкой failure, верните z.
11. Вернуть c(x).

 

Примечание 1

Атом Atom, за которым следует квантификатор Quantifier, повторяется то количество раз, которое указанно квантификатором. Квантификатор может быть «не жадным» (non-greedy), и в этом случае шаблон Атома Atom повторяется как можно меньше раз, все еще совпадая с продолжением, или он может быть «жадным«, и в этом случае шаблон Atom повторяется столько раз, сколько возможно, все еще сопоставляя продолжение. Шаблон Atom повторяется, вместо того чтобы сопоставлять последовательность входных символов, которой он соответствует, поэтому разные повторения Atom могут соответствовать разным входным подстрокам.

 

Примечание 2

Если Атом Atom и продолжение регулярного выражения имеют точки выбора (choice points), Атом Atom сначала сопоставляется столько раз (или как можно меньше, если не является жадным), насколько это возможно. Все варианты в «продолжении» (sequel) проверяются перед переходом к следующему варианту в последнем повторении Атома Atom. Все варианты выбора в последнем (n-ом) повторении Атома Atom проверяются перед переходом к следующему варианту выбора в предпоследнем (n — 1)-ом повторении Atom; в этот момент может оказаться, что теперь возможно больше или меньше повторений Atom; они исчерпываются (опять же, начиная с минимального или максимального количества) перед переходом к следующему выбору в (n — 1)-ом повторении Atom и так далее.

Сравните

/a[a-z]{2,4}/.exec("abcdefghi")

который возвращает «abcde» с

/a[a-z]{2,4}?/.exec("abcdefghi")

который возвращает «abc«.

 

Учтите также

/(aa|aabaac|ba|b|c)*/.exec("aabaac")

который, в соответствии с указанным выше порядком точек выбора, возвращает массив

["aaba", "ba"]

а не какой-либо из:

["aabaac", "aabaac"]

["aabaac", "c"]

Вышеупомянутый порядок точек выбора можно использовать для написания регулярного выражения, которое вычисляет наибольший общий делитель двух чисел (представленных в унарной записи). В следующем примере вычисляется «Greatest Common Divisor — GCD» или «Наибольший общий делитель — НОД«10 и 15:

"aaaaaaaaaa,aaaaaaaaaaaaaaa".replace(/^(a+)\1*,\1+$/, "$1")

который возвращает НОД в унарной записи «aaaaa«.

 

Примечание 3

Шаг 4 RepeatMatcher очищает захваты Атома Atom каждый раз, когда Атом Atom повторяется. Мы можем увидеть его поведение в регулярном выражении

/(z)((a+)?(b+)?(c))*/.exec("zaacbbbcac")

который возвращает массив

["zaacbbbcac", "z", "ac", "a", undefined, "c"]

и не

["zaacbbbcac", "z", "ac", "a", "bbb", "c"]

потому что каждая итерация самого внешнего * очищает все захваченные строки, содержащиеся в количественно определенном Атоме Atom, который в этом случае включает строки захвата с номерами 2, 3, 4 и 5.

 

Примечание 4

Шаг 2.b RepeatMatcher заявляет, что после того, как минимальное количество повторений было удовлетворено, любые дополнительные расширения Атома Atom, которые соответствуют пустой последовательности символов, не рассматриваются для дальнейших повторений. Это предотвращает попадание обработчика регулярных выражений в бесконечный цикл для таких шаблонов, как:

/(a*)*/.exec("b")

или чуть более сложный:

/(a*)b\1+/.exec("baaaac")

который возвращает массив

["b", ""]

 

Информационные ссылки

Стандарт ECMAScript — Раздел «Term» — https://tc39.es/ecma262/#sec-term