re2c es un generador de lexer gratuito y de código abierto para C , C++ , Go y Rust . Compila especificaciones declarativas de expresiones regulares en autómatas finitos deterministas . Escrito originalmente por Peter Bumbulis y descrito en su artículo, [1] re2c pasó a ser de dominio público y desde entonces ha sido mantenido por voluntarios. [3] Es el generador lexer adoptado por proyectos como PHP , [4] SpamAssassin , [5] Ninja build system [6] y otros. Junto con el generador de analizador Lemon , re2c se utiliza en BRL-CAD . [7] Esta combinación también se utiliza con STEPcode, una implementación del estándar ISO 10303 . [8]
El objetivo principal de re2c es generar lexers rápidos : [1] al menos tan rápidos como los lexers C razonablemente optimizados codificados a mano. En lugar de utilizar el enfoque tradicional basado en tablas, re2c codifica la máquina de estados finitos generada directamente en forma de saltos condicionales y comparaciones. El programa resultante es más rápido que su homólogo basado en tablas [1] y mucho más fácil de depurar y comprender. Además, este enfoque a menudo da como resultado lexers más pequeños, [1] ya que re2c aplica una serie de optimizaciones, como la minimización de DFA y la construcción de túneles automáticos. [9] Otra característica distintiva de re2c es su interfaz flexible: en lugar de asumir una plantilla de programa fija, re2c permite al programador escribir la mayor parte del código de la interfaz y adaptar el lexer generado a cualquier entorno particular. La idea principal es que re2c debería ser una abstracción de costo cero para el programador: su uso nunca debería resultar en un programa más lento que la implementación correspondiente codificada a mano.
El programa re2c puede contener cualquier número de /*!re2c ... */
bloques. Cada bloque consta de una secuencia de reglas , definiciones y configuraciones
(se pueden entremezclar, pero generalmente es mejor poner primero las configuraciones, luego las definiciones y luego las reglas). Las reglas tienen la forma REGEXP { CODE }
o REGEXP := CODE;
donde REGEXP
es una expresión regular y CODE
es un bloque de código C. Cuando REGEXP
coincide con la cadena de entrada, el flujo de control se transfiere al archivo asociado CODE
. Hay una regla especial: la regla predeterminada con *
en lugar de REGEXP
; se activa si no coincide ninguna otra regla. re2c tiene una semántica de coincidencia codiciosa : si coinciden varias reglas, se prefiere la regla que coincida con el prefijo más largo; si las reglas en conflicto coinciden con el mismo prefijo, la regla anterior tiene prioridad. Las definiciones tienen la forma NAME = REGEXP;
(y también NAME { REGEXP }
en modo de compatibilidad Flex ). Las configuraciones tienen la forma re2c:CONFIG = VALUE;
donde CONFIG
está el nombre de la configuración particular y VALUE
es un número o una cadena. Para un uso más avanzado, consulte el manual oficial de re2c. [22]
re2c utiliza la siguiente sintaxis para expresiones regulares:
"foo"
literal de cadena que distingue entre mayúsculas y minúsculas'foo'
literal de cadena que no distingue entre mayúsculas y minúsculas[a-xyz]
, [^a-xyz]
clase de personaje (posiblemente negada).
cualquier carácter excepto nueva líneaR \ S
diferencia de clases de personajesR*
cero o más ocurrencias deR
R+
una o más apariciones deR
R?
opcionalR
R{n}
repetición de tiempos R
exactosn
R{n,}
repetición de R
al menos n
vecesR{n,m}
repetición de R
de n
a m
veces(R)
justo R
; Los paréntesis se utilizan para anular la precedencia o para la subcoincidencia de estilo POSIX.R S
concatenación: R
seguida deS
R | S
alternativa: R
oS
R / S
anticipación: R
seguido de S
, pero S
no se consumename
la expresión regular definida como name
(excepto en el modo de compatibilidad Flex )@stag
una etiqueta s : guarda la última posición de entrada en la que @stag
coincide en una variable llamadastag
#mtag
una etiqueta m : guarda todas las posiciones de entrada en las que #mtag
coincide en una variable denominadamtag
Las clases de caracteres y cadenas literales pueden contener las siguientes secuencias de escape: , \a
, \b
, \f
, \n
, \r
, \t
, \v
escapes \\
octales \ooo
y hexadecimales \xhh
y .\uhhhh
\Uhhhhhhhh
Aquí hay un programa muy simple en re2c (ejemplo.re). Comprueba que todos los argumentos de entrada sean números hexadecimales. El código para re2c está incluido en comentarios /*!re2c ... */
, todo el resto es código C simple . Consulte el sitio web oficial de re2c para ver ejemplos más complejos. [23]
#incluir <stdio.h> static int lex ( const char * YYCURSOR ) { const char * YYMARKER ; /*!re2c re2c:define:YYCTYPE = char; re2c:yyfill:habilitar = 0; fin = "\x00"; hexadecimal = "0x" [0-9a-fA-F]+; * { printf("err\n"); devolver 1; } extremo hexadecimal { printf("hexadecimal\n"); devolver 0; } */ }int main ( int argc , char ** argv ) { para ( int i = 1 ; i < argc ; ++ i ) { lex ( argv [ i ]); } devolver 0 ; }
Dado eso, re2c -is -o example.c example.re
genera el siguiente código (ejemplo.c). El contenido del comentario /*!re2c ... */
se sustituye por un autómata finito determinista
codificado en forma de saltos condicionales y comparaciones; el resto del programa se copia palabra por palabra en el archivo de salida. Hay varias opciones de generación de código; normalmente re2c usa switch
declaraciones, pero puede usar if
declaraciones anidadas (como en este ejemplo con -s
la opción) o generar mapas de bits y tablas de salto. Qué opción es mejor depende del compilador de C; Se anima a los usuarios de re2c a experimentar.
/* Generado por re2c 1.2.1 el viernes 23 de agosto 21:59:00 2019 */ #include <stdio.h> static int lex ( const char * YYCURSOR ) { const char * YYMARKER ; { char yych ; yych = * YYCURSOR ; si ( yych == '0' ) ir a yy4 ; ++ YCURSOR ; yy3 : { printf ( "err \n " ); devolver 1 ; } yy4 : yych = * ( YYMARKER = ++ YYCURSOR ); si ( yych ! = 'x' ) ir a yy3 ; yych = *++ YYCURSOR ; si ( yych >= 0x01 ) pasa a yy8 ; yy6 : YYCURSOR = YYMARKER ; ir a yy3 ; yy7 : yych = *++ YYCURSOR ; yy8 : if ( yych <= '@' ) { if ( yych <= 0x00 ) ir a yy9 ; si ( yych <= '/' ) ir a yy6 ; si ( yych <= '9' ) ir a yy7 ; ir a yy6 ; } else { if ( yych <= 'F' ) ir a yy7 ; if ( yych <= '`' ) ir a yy6 ; if ( yych <= 'f' ) ir a yy7 ; ir a yy6 ; } yy9 : ++ YYCURSOR ; { printf ( "hexadecimal \n " ); devolver 0 ; } } }int main ( int argc , char ** argv ) { para ( int i = 1 ; i < argc ; ++ i ) { lex ( argv [ i ]); } devolver 0 ; }