Nota: Depois de tentar isso sozinho, logo percebi o que era um erro. Portanto, estou modificando um pouco as regras.
A funcionalidade mínima necessária:
- Classes de personagens (
.
,\w
,\W
, etc.) - Multiplicadores (
+
,*
, e?
) - Grupos de captura simples
Seu desafio é implementar o PCRE no idioma de sua escolha, sujeito às seguintes condições:
- Você não pode usar as instalações RegEx nativas do seu idioma de forma alguma . Você também não pode usar uma biblioteca RegEx de terceiros.
- Sua entrada deve implementar o máximo das especificações do PCRE. que possível.
Seu programa deve aceitar como entrada, 2 linhas:
- a expressão regular
- a entrada de string para combinar
Seu programa deve indicar em sua saída:
- Se o RegEx correspondeu a qualquer lugar da sequência de entrada
- Os resultados de qualquer grupo de captura
O vencedor será a entrada que implementa o máximo de especificações. que possível. Em caso de empate, o vencedor será a inscrição mais criativa, conforme julgado por mim.
Edit: para esclarecer algumas coisas, aqui estão alguns exemplos de entrada e saída esperada:
- Entrada:
^ \ s * (\ w +) $ Olá
- Resultado:
Correspondências: sim Grupo 1: 'olá'
- Entrada:
(\ w +) @ (\ w +) (?: \ .com | \ .net) [email protected]
- Resultado:
Correspondências: sim Grupo 1: 'sam' Grupo 2: 'teste'
code-challenge
regular-expression
Nathan Osman
fonte
fonte
Respostas:
Pitão
Como implementar o PCRE completo é demais, implementei apenas um subconjunto essencial dele.
Suporta
|.\.\w\W\s+*()
. Regexp de entrada deve estar correto.Exemplos:
Como funciona:
Para uma teoria detalhada, leia esta Introdução à Teoria dos Autômatos, Idiomas e Computação .
A idéia é converter a expressão regular original em um autômato finito não-determinista (NFA). Na verdade, as expressões regulares do PCRE são pelo menos gramáticas livres de contexto para as quais precisamos de autômatos push-down, mas nos limitaremos a um subconjunto do PCRE.
Autômatos finitos são gráficos direcionados nos quais os nós são estados, as arestas são transições e cada transição possui uma entrada correspondente. Inicialmente, você inicia a partir de um nó inicial, predefinido. Sempre que você recebe uma entrada que corresponde a uma transição, você a transfere para um novo estado. Se você alcançou um nó de terminal, é chamado de entrada automática aceita. No nosso caso, a entrada é uma função correspondente que retorna true.
Eles são chamados de autômatos não deterministas porque, às vezes, há transições mais correspondentes que você pode realizar do mesmo estado. Na minha implementação, toda transição para o mesmo estado deve corresponder à mesma coisa, então eu armazenei a função de correspondência junto com o estado de destino (
states[dest][0]
).Transformamos nossa regexp em um autômato finito usando blocos de construção. Um bloco de construção possui um nó inicial (
first
) e um nó final (last
) e corresponde a algo do texto (possível sequência vazia).Os exemplos mais simples incluem
True
(first == last
)c == txt[pos]
(first == last
)(
first == last`)Você também precisará da nova posição no texto onde corresponder ao próximo token.
Exemplos mais complicados são (letras maiúsculas representam blocos).
B + correspondente:
combinando A | B | C:
Todos os operadores regexp podem ser transformados assim. Apenas tente
*
.A última parte é analisar o regexp, que requer uma gramática muito simples:
Espero que implementar uma gramática simples (acho que seja LL (1), mas me corrija se estiver errado) é muito mais fácil do que criar um NFA.
Depois de ter o NFA, você precisa voltar atrás até chegar ao nó do terminal.
Código fonte (ou aqui ):
fonte
(a+b)+
correspondeabaabaaabaaaab
.