Комментарии 14
В функции match_from_pos(ition) находится основной механизм регулярных выражений основанный на применении рекурсии.
Всегда считал, что для этого используются конечные автоматы.
Код с конечными автоматами получается хорошо структуированным и легко читается.
Это так и есть. Тут уже зависит от работодателя - примет ли он решение на самодельном велосипеде или таки откажет из-за того, что не использован годами проверенный инструмент (НКА).
Да, конечные автоматы это первый и один из стандартных механизмов реализации регулярных выражений. Первый подобный алгоритм был написам Кеном Томпсоном и строил конечный автомат, а после чего на лету компилировал его напрямую в машинные коды. Есть еще варианты со стековой виртуальной машиной, но они все достоточно громоздкие для этой задачи. На хабре кстати есть перевод хорошей статьи по этому поводу https://habr.com/ru/company/mailru/blog/270507/
В конкретно упомянутой постановке задача допускает гораздо более простое решение. Единственный элемент, который действительно сопоставляется - это точка "."
(в настоящих регулярных выражениях есть еще "один из символов" "[a-z]"
и "один символ кроме перечисленых" "[^a-z]"
например. Поэтому регулярное выражение из символов "^$.*?+" это указание, сколько точек нужено найти, возможно с привязкой к началу/концу строки.
Конкретнее, регулярное выражение это строка следующего вида:
"^"
- начало строки, либо есть, либо нет. Пока пропускаем.Последовательность из
"."
,".*"
,".?"
,".+". В этой последовательности каждая часть - это просто указание, от скольки до скольки произвольных символов может подойти. Точнее:
"."
- минимум 1 символ, максимум 1 символ, далее -(1, 1)
;".*"
-(0, ∞)
;".?"
-(0, 1)
;".+"
-(1, ∞)
;
"$"
- конец строки, либо есть либо нет. Пока пропускаем.
Для пар (минимум, максимум) в регулярном выражении считаем сумму всех пар (допускаем, что ∞ + x = ∞
для любого x
). Получаем (min, max).
В итоге регулярное выражение - это указание, что в строке от min
до max
символов в строке, возможно с привязкой к началу/концу. Дальше можно разобрать случаи с началом/концом:
Есть и
"^"
, и"$"
. Тогда считаем количество символов в строке, еслиmin <= len <= max
, то вся строка и подходит, иначе совпадения нет;Есть только
"^"
. Тогда возвращаемmin
первых символов, если столько есть, иначе совпадения нет.Есть только
"$"
. Тогда возвращаемmin
последних символов, если столько есть, иначе совпадения нет.Нет ни
"^"
, ни"$"
. Возвращаемmin
любых символов (например первых) если есть, иначе совпадения нет.
P.S. Заметил, что не используется max
, его можно использовать, чтобы прикрутить при желании нахождение всех совпадений.
Ещё сопоставляются все буквы. Например, регулярке a* строка aaa удовлетворяет, а строка bbb (той же длинны) - нет.
Не могли бы вы привести пример хотя бы псевдокода, если у этой задачи есть гораздо более простое решение, так как я тоже не совсем понял, как это будет работать в связке с обычными символами. Возможно я просто не до конца понял ваш принцип.
А как группы символов и выбор вариантов вида (?:abc|abf|$)
?
А разве в статье написано про их реализацию? Мог бы быть заголовок "Пишем полный аналог PCRE в 70 строк кода" :)
Односимвольный выбор вариантов можно достаточно просто добавть по аналогии с имеющимися метасимволами, а для поддержки групп нужно добавить стек, чтобы сохранять позиции для бэктрекинга
<?php
if (match_metachar($re_tokens, $re_pos + 1, "|"))
return match_from_pos($re_tokens, $re_pos + 2, $str, $str_pos) || (!is_end($str, $str_pos) &&
match_char($re_tokens[$re_pos], $str[$str_pos]) &&
match_from_pos($re_tokens, $re_pos + 3, $str, $str_pos + 1));
ну и добавть "|" в массив META_CHARS
Тогда можно было бы свести задачу к wildcard-символам типа * и _ в именах файлов или % с _ в SQL. Задача та же, но более применима в реальной жизни, чем некоторое ограниченное подмножество регулярок, из которого отрезано всё полезное.
О боже, я в красках представил себе производительность.
Реализация простого механизма регулярных выражений в 70 строк кода