Примечание: попробовав это сам, я вскоре понял, что это за ошибка. Поэтому я немного изменяю правила.
Минимально необходимый функционал:
- Классы символов (
.
,\w
,\W
и т.д.) - Множители (
+
,*
и?
) - Простые группы захвата
Ваша задача - реализовать PCRE на выбранном вами языке при соблюдении следующих условий:
- Вы не можете использовать собственные средства RegEx вашего языка в любом случае . Вы также не можете использовать стороннюю библиотеку RegEx.
- Ваша заявка должна реализовывать как можно больше спецификации PCRE. насколько это возможно.
Ваша программа должна принять в качестве ввода 2 строки:
- регулярное выражение
- строка ввода для сравнения
Ваша программа должна указать в своем выводе:
- Соответствует ли RegEx где-либо во входной строке
- Результаты любых групп захвата
Победителем должна стать запись, которая реализует как можно большую часть спецификации. насколько это возможно. В случае ничьей победитель должен быть самым креативным, как мне кажется.
Изменить: чтобы уточнить некоторые вещи, вот несколько примеров ввода и ожидаемого вывода:
- Входные данные:
^ \ S * (\ ш +) $ Здравствуйте
- Выход:
Матчи: да Группа 1: «Привет»
- Входные данные:
(\ Ш +) @ (\ W +) (?:. \ Ком | \ .net) sam@test.net
- Выход:
Матчи: да Группа 1: «Сэм» Группа 2: «тест»
code-challenge
regular-expression
Натан Осман
источник
источник
Ответы:
питон
Поскольку реализация полного PCRE - это слишком много, я реализовал только его существенную часть.
Опоры
|.\.\w\W\s+*()
. Входное регулярное выражение должно быть правильным.Примеры:
Как это устроено:
Для детальной теории прочитайте это Введение в теорию автоматов, языков и вычислений .
Идея состоит в том, чтобы преобразовать исходное регулярное выражение в недетерминированный конечный автомат (NFA). На самом деле регулярные выражения PCRE - это, по крайней мере, контекстно-свободные грамматики, для которых нам нужны автоматные выпадающие меню, но мы ограничимся подмножеством PCRE.
Конечные автоматы - это ориентированные графы, в которых узлы - это состояния, ребра - это переходы, и каждый переход имеет соответствующий вход. Первоначально вы начинаете с начального узла, предопределенного. Всякий раз, когда вы получаете вход, соответствующий одному из переходов, вы переходите в новое состояние. Если вы достигли терминального узла, это называется, что автоматы приняли ввод. В нашем случае input - это функция соответствия, которая возвращает true.
Они называются недетерминированными автоматами, потому что иногда есть больше подходящих переходов, которые вы можете взять из одного и того же состояния. В моей реализации все переходы в одно и то же состояние должны совпадать, поэтому я сохранил соответствующую функцию вместе с целевым состоянием (
states[dest][0]
).Мы преобразуем наше регулярное выражение в конечный автомат, используя строительные блоки. Строительный блок имеет начальный узел (
first
) и конечный узел (last
) и соответствует чему-то из текста (возможно, пустая строка).Простейшие примеры включают
True
(first == last
)c == txt[pos]
(first == last
)(
first == last`)Вам также понадобится новая позиция в тексте, где будет соответствовать следующий токен.
Более сложные примеры (заглавные буквы обозначают блоки).
соответствие B +:
соответствие A | B | C:
Все операторы регулярных выражений могут быть преобразованы следующим образом. Просто попробуйте
*
.Последняя часть - это синтаксический анализ регулярного выражения, который требует очень простой грамматики:
Надеюсь, реализовать простую грамматику (я думаю, что это LL (1), но поправьте меня, если я ошибаюсь) гораздо проще, чем создать NFA.
Когда у вас есть NFA, вам нужно возвращаться назад, пока вы не достигнете конечного узла.
Исходный код (или здесь ):
источник
(a+b)+
спичкиabaabaaabaaaab
.