Nota: Después de probar esto yo mismo, pronto me di cuenta del error que era. Por lo tanto, estoy modificando un poco las reglas.
La funcionalidad mínima requerida:
- Las clases de caracteres (
.
,\w
,\W
, etc.) - Los multiplicadores (
+
,*
y?
) - Grupos de captura simples
Su desafío es implementar PCRE en el idioma de su elección sujeto a las siguientes condiciones:
- No puede utilizar las instalaciones RegEx nativas de su idioma de ninguna manera . Tampoco puede usar una biblioteca RegEx de terceros.
- Su entrada debe implementar tanto de la especificación PCRE. como sea posible.
Su programa debe aceptar como entrada, 2 líneas:
- la expresión regular
- la entrada de cadena para que coincida con
Su programa debe indicar en su salida:
- Si el RegEx coincide en alguna parte de la cadena de entrada
- Los resultados de cualquier grupo de captura
El ganador será la entrada que implemente la mayor parte de la especificación. como sea posible. En caso de empate, el ganador será la entrada más creativa, según mi criterio.
Editar: para aclarar algunas cosas, aquí hay algunos ejemplos de entrada y salida esperada:
- Entrada:
^ \ s * (\ w +) $ Hola
- Salida:
Coincidencias: sí Grupo 1: 'hola'
- Entrada:
(\ w +) @ (\ w +) (?: \. com | \ .net) [email protected]
- Salida:
Coincidencias: sí Grupo 1: 'sam' Grupo 2: 'prueba'
code-challenge
regular-expression
Nathan Osman
fuente
fuente
Respuestas:
Pitón
Como implementar PCRE completo es demasiado, implementé solo un subconjunto esencial del mismo.
Soportes
|.\.\w\W\s+*()
. La expresión regular de entrada debe ser correcta.Ejemplos:
Cómo funciona:
Para una teoría detallada, lea esta Introducción a la teoría de los autómatas, los idiomas y la computación .
La idea es convertir la expresión regular original en un autómata finito no determinista (NFA). En realidad, las expresiones regulares de PCRE son al menos gramáticas libres de contexto para las cuales necesitamos autómatas pushdown, pero nos limitaremos a un subconjunto de PCRE.
Los autómatas finitos son gráficos dirigidos en los que los nodos son estados, los bordes son transiciones y cada transición tiene una entrada coincidente. Inicialmente comienza desde un nodo de inicio, predefinido. Cada vez que recibe una entrada que coincide con una de las transiciones, lleva esa transición a un nuevo estado. Si llegó a un nodo terminal, se llama entrada de autómata aceptada. En nuestro caso, la entrada es una función coincidente que devuelve verdadero.
Se llaman autómatas no deterministas porque a veces hay más transiciones coincidentes que puede tomar del mismo estado. En mi implementación, toda transición al mismo estado debe coincidir con la misma cosa, por lo que almacené la función de coincidencia junto con el estado de destino (
states[dest][0]
).Transformamos nuestra expresión regular en un autómata finito utilizando bloques de construcción. Un bloque de creación tiene un nodo inicial (
first
) y un nodo final (last
) y coincide con algo del texto (posible cadena vacía).Los ejemplos más simples incluyen
True
(first == last
)c == txt[pos]
(first == last
)(
first == last`)También necesitará la nueva posición en el texto donde coincidir con el próximo token.
Ejemplos más complicados son (las letras mayúsculas representan bloques).
B + a juego:
coincidencia A | B | C:
Todos los operadores regexp pueden transformarse así. Solo inténtalo
*
.La última parte es analizar la expresión regular que requiere una gramática muy simple:
Con suerte, implementar una gramática simple (creo que es LL (1), pero corrígeme si me equivoco) es mucho más fácil que construir un NFA.
Una vez que tenga el NFA, debe retroceder hasta llegar al nodo terminal.
Código fuente (o aquí ):
fuente
(a+b)+
coincideabaabaaabaaaab
.