Reglas de Raku
Las reglas de Raku son expresiones regulares, coincidencia de patrones e interpretación de propósito general del lenguaje de programación Raku, y son una parte principal del lenguaje. Debido a que las construcciones de coincidencia de patrones han excedido, desde hace un tiempo, las capacidades formales de las expresiones regulares, la documentación de Raku se refiere a ellas exclusivamente como regexes, distanciándose el término de la definición formal.
Raku ofrece un superconjunto de las características de Perl 5 con respecto a las regexes, alojándolas en un gran entorno de trabajo llamado reglas (rules), que ofrecen las características de interpretación gramática de expresiones, así como actuar como clausura con respecto a su ámbito léxico.[1] Las reglas se presentan con la palabra clave rule, que tiene un uso similar a la definición de subrutinas. Las reglas anónimas se presentan con el identificador regex (o rx), o simplemente usarse en línea como las regexes de Perl 5 por medio de los operadores m (coincidencia) o s (sustitución).
Historia
En Apocalipsis 5, un documento que define las decisiones preliminares sobre la coincidencia de patrones en Raku, Larry Wall enumeró 20 problemas con la «actual cultura regex». Entre estos estaban ser «demasiado compactos y 'lindos'», tenían «demasiada confianza en muy pocos metacaracteres», «poco soporte para las capturas con nombre», «poco soporte para las gramáticas», y «escasa integración con [el] lenguaje 'real'».[2]
Entre el final de 2004 y mediados de 2005, se desarrolló un compilador de reglas de Raku para la máquina virtual Parrot llamado Motor de Análisis Sintáctico (PGE), que más tarde fue reescrito a una forma más genérica, Analizador del motor gramático (Parser Grammar Engine). PGE es una combinación de tiempo de ejecución y compilador para las gramáticas Raku que permite a cualquier compilador basado en parrot usar estas herramientas para el análisis, y también ofrece reglas en tiempos de ejecución.
Además de otras características de Raku, el soporte para capturas con nombre se incorporó a Perl 5.10 en el 2007.[3]
En mayo de 2012, la referencia sobre la implementación de Raku, Rakudo, empaqueta en su distribución mensual Rakudo Star un intérprete funcional de JSON construido complementamente con reglas Raku.[4]
Cambios desde Perl 5
Sólo hay seis características que no cambian con respecto a las regexes de Perl 5:
- Literales: caracteres de palabra (letras, números y guion bajo) coincidiendo literalmente
- Captura: (...)
- Alternativas: |
- Escapado con barra inversa: \
- Cuantificadores de repetición: *, +, y ?, pero no {m,n}
- Sufijos para coincidencia mínima: *?, +?, ??
Algunas de las adiciones más poderosas son:
- La habilidad de referenciar reglas usando <nombre_de_la_regla> para construir gramáticas enteras.
- Un puñado de operadores que permiten al programador controlar la vuelta atrás durante las coincidencias.
Los siguientes cambios mejoran considerablemente la legibilidad de expresiones regulares:
- Grupos de no-captura simplificados: [...], que es lo mismo que en Perl 5: (?:...)
- Aserciones de código simplificadas: <?{...}>
- Permite que el espacio en blanco pueda incluirse sin que coincida, permitiendo regexes multilínea. Use \ o para expresar espacio en blanco.
- Formateado extendido de las expresiones regulares (la opción /x de Perl 5) está activa por defecto.
Cambios implícitos
Algunas de las características de las expresiones regulares de Perl 5 se vuelven más poderosas en Raku debido a su habilidad de encapsular las características expandidas de las reglas de Raku. Por ejemplo, en Perl 5, los operadores de cercanía positivos y negativos (?=...) y (?!...). En Raku estas mismas características existen, pero se llaman <before ...> y <!before ...>.
Además, como before puede encapsular reglas de forma arbitraria, puede usarse para expresar cercanía como un predicado sintáctico para una gramática. Por ejemplo, la siguiente expresión de análisis gramatical describe el clásico lenguaje de contexto no libre :
S ← &(A !b) a+ B A ← a A? b B ← b B? c
En reglas de Raku esto sería:
rule S { <before <A> <!before b>> a+ <B> } rule A { a <A>? b } rule B { b <B>? c }
Naturalmente, dada la habilidad de mezclar reglas y código normal, esto se puede simplificar en esto:
rule S { (a+) (b+) (c+) <{$0.elems == $1.elems == $2.elems}> }
Sin embargo, esto hace uso de las aserciones, que es un concepto ligeramente diferente de las reglas de Raku, pero sustancialmente más diferentes en teoría de análisis, creando una semántica en lugar de un predicado sintáctico. La mayor diferencia en la práctica es el rendimiento. No hay forma de que el motor de reglas conozca las condiciones en que las aserciones puedan coincidir, por lo que no se realiza ninguna optimización de este proceso.
Integración con Perl
En muchos lenguajes, las expresiones regulares son meten como cadenas, que son pasan entonces a las rutinas de las bibliotecas que las analizan y compilan en un estado interno. En Perl 5, las expresiones regulares comparten algo del analizador léxico con el escáner de Perl. Esto simplifica muchas aspectos del uso de las expresiones regulares, a pesar de añadir mucha complejidad al escáner. En Raku, las reglas son parte de la gramática del lenguaje. No existe un analizador separado para las reglas, como lo hay en Perl 5. Esto quiere decir que el código, incrustado en las reglas, se analiza al mismo tiempo que la propia regla y el código que la rodea. Por ejemplo, es posible anidar reglas y código sin reinvocar el analizador:
rule ab { (a.) # coincide con una «a» seguida de un carácter cualquiera # Entonces comprueba si el carácter era una «b» # Si es así, imprime un mensaje { $0 ~~ /b {say «encontrada la b»}/ } }
Lo anterior es un único bloque de código Raku que contiene una definición exterior de una regla, un bloque interior con código de aserción, y dentro de él, una expresión regular que contiene un nivel más de aserción.
Implementación
Identificadores
Existen distintos identificadores que se usan en conjunción con las reglas de Raku:
- regex
- Una regex, anónima o no, que por defecto ignora el espacio en blanco dentro de la regex.
- token
- Una regex, anónima o no, que implica al modificador :ratchet.
- rule
- Una regex, anónima o no, que implica a los modificadores :ratchet y :sigspace.
- rx
- Una regex anónima que toma delimitadores arbitrarios, como por ejemplo // mientras que regex solo usa llaves.
- m
- Una forma de operador de regex anónima que realiza coincidencias con delimitadores arbitrarios.
- mm
- Abreviatura para m con el modificador :sigspace.
- s
- Una forma de operador de regex anónima que realiza sustitución con delimitadores arbitrarios.
- ss
- Abreviatura para s con el modificador :sigspace.
- /.../
- Colocar simplemente una regex entre barras es una abreviatura para rx/.../.
Aquí hay un ejemplo de un uso típico:
token palabra { \w+ } rule frase { <palabra> [ \, <palabra> ]* \. } if $cadena ~~ / <frase> \n / { ... }
Modificadores
Los modificadores pueden colocarse después de cualquier identificador de regex, y antes del delimitador. Si una regex tiene nombre, el modificador viene después del nombre. Los modificadores controlan la forma en que las regex se analizan y cómo se comportan. Son siempre presentadas con un precedente carácter :.
Algunos de los modificadores más importantes son:
- :i o :ignorecase – Realiza una coincidencia sin importar el tamaño de caja.
- :m o :ignoremark – Realiza una coincidencia sin importar la combinación de caracteres.
- :g o :global – Realiza la coincidencia más de una vez para una cadena dada.
- :s o :sigspace – Reemplaza el espacio en blanco en la regex con una regla de coincidencia de espacio en blanco, en lugar de simplemente ignorarlo.
- :Perl5 – Trata la regex como una expresión regular de Perl 5.
- :ratchet – No realizar la vuelta atrás en la regla.
Por ejemplo:
regex adición :ratchet :sigspace { <término> \+ <expresión> }
Gramática
Una gramática se puede definir usando el operador grammar. Una gramática es esencialmente un espacio de nombres para las reglas:
grammar Str::SprintfFormat { regex format_token { \%: <indice>? <precision>? <modificador>? <directiva> } token indice { \d+ \$ } token precision { <indicadores>? <vector>? <contador_precision> } token indicadores { <[\ +0\#\-]>+ } token contador_precision { [ <[1-9]>\d* | \* ]? [ \. [ \d* | \* ] ]? } token vector { \*? v } token modificador { ll | <[lhmVqL]> } token directiva { <[\%csduoxefgXEGbpniDUOF]> } }
Esta es la gramática que se usa para definir la notación de formateo de cadenas para sprintf.
Fuera de este espacio de nombres, se puede usar estas reglas así:
if / <Str::SprintfFormat::format_token> / { ... }
Una regla utilizada de esta manera es actualmente idéntica a la invocación de una subrutina con la semántica y efectos colaterales extras de la coincidencia de patrones (por ejemplo, las invocaciones de reglas pueden volver hacia atrás).
Ejemplos
Aquí hay algunos ejemplos de reglas en Raku:
rx { a [ b | c ] ( d | e ) f : g } rx { ( ab* ) <{ $1.size % 2 == 0 }> }
Esta última es idéntica a:
rx { ( ab[bb]* ) }
Referencias
Enlaces externos
- Esta obra contiene una traducción automática parcial derivada de «Raku rules» de Wikipedia en inglés, concretamente de esta versión, publicada por sus editores bajo la Licencia de documentación libre de GNU y la Licencia Creative Commons Atribución-CompartirIgual 4.0 Internacional.
- Raku Grammars - La página de manual de referencia para las gramáticas.
- Grammar tutorial - Un tutorial para gramáticas en Raku.
- Synopsis 05 - El documento estándar que cubre las regexes y reglas para Perl 6.
- Perl 6 Regex Introduction - Introducción amable a las regexes de Perl 6.
Datos: Q7169108