Uso divertido del compilador Ragel State Machine para crear una función de análisis de línea en int argc, char * argv [].
Todo comenzó cuando necesitábamos la función buildargv para analizar una cadena para luego pasarla a
int main (int argc, char *argv[]) { body }
Bueno, pensé, no puede ser que fuera imposible pedir prestado en ningún lado, ahora encontramos ... Y no encontré ...

Bueno, no es que no lo hubiera encontrado en absoluto, por ejemplo, https://github.com/gcc-mirror/gcc/blob/master/libiberty/argv.c (GPLv2 siempre es bueno), asumo inmediatamente tales obligaciones No estaba listo. Definitivamente existe una función de este tipo en bash (GPLv3 es aún mejor). zsh? - Ve a buscar (encontré ... - No quiero).
En general, no encontré lo que buscaba, pero no me gustó lo que encontré. Bueno, al final, tengo derecho a ello, de todos modos, me hago sed de entretenimiento en el proceso.
No quería escribir este caso de una manera convencional a partir de la palabra, incluso estaba molesto por este motivo.
En general, nos encontramos con el compilador Ragel State Machine.
Kit de herramientas
- gcc;)
- ragel
- hacer
- lcov
- libcheck
El proyecto se puede encontrar aquí: JOYFUL CMDLINE PARSER ESCRITO EN RAGEL
Declaración del problema.
En la entrada, tenemos una cadena de cualquier tipo, la tarea es obtener de la cadena una matriz de argumentos separados por un espacio o pestaña, con:
- Cualquier carácter que siga al carácter de escape
\
debe ignorarse. - Cualquier personaje que esté entre dos dobles o debe
ser considerado un elemento - En caso de
'
o "
cerrado, se devolverá un error
En general, no hay muchas condiciones. Y Ragel es bastante adecuado para esta tarea.
Implementación explicada
Declaremos una máquina con el nombre "buildargv" y solicitemos a Ragel que coloque sus datos al principio del archivo (5.8.1 Escribir datos).
%%{ machine buildargv; write data; }%%
A continuación, declaramos una máquina lineElement
, que a su vez consiste en combinar (2.5.1 Unión) dos máquinas: arg
y whitespace
.
lineElement = arg >start_arg %end_arg | whitespace; main := blineElements**;
En la entrada y salida de la máquina arg
, end_arg
las acciones start_arg
y end_arg
respectivamente.
action start_arg { argv_s = p; } action end_arg { nargv = (char**)realloc((*argv), (argc_ + 1)*sizeof(char*)); (*argv) = nargv; (*argv)[argc_] = strndup(argv_s, p - argv_s); argc_++; }
Además, la tarea start_arg
para guardar la posición del personaje en la entrada y la tarea end_arg
agregan un nuevo elemento a la matriz argv
, en caso de salida exitosa de la máquina arg
.
Ahora echemos un vistazo más de cerca a arg
.
arg = '\''> { fcall squote; } | '"'>{ fcall dquote; } | ( '\\'>{fcall skip;} | ^[ \t"'\\] )+;
Consiste en una unión de tres máquinas '
, "
y (\ | ^[ \t"'\])
, esta última es una unión de \
y ^[ \t"'\]
respectivamente.
Cuando encontramos el carácter '
lo llamamos squote
'
, lo llamamos squote
, o si el carácter actual es \
llamamos skip
, que omite cualquier carácter que lo siga, y cualquier carácter no es 0x20
(espacio), 0x09
(tab), '
, "
o \
se considera correcto .
Queda por considerar una parte muy pequeña:
skip := any @{ fret; }; dquote := ( '\\'>{ fcall skip; } | ^[\\] )+ :> ["] @{ fret; } @err(dquote_err); squote := ( '\\'>{ fcall skip; } | ^[\\] )+ :> ['] @{ fret; } @err(squote_err);
Con skip
ya hemos descubierto qué hace ^['\\]
tampoco debería causar preguntas. Y aquí :>
esta es la Entry-Guarded Concatenation
(4.2 operadores protegidos que encapsulan prioridades) su significado es que la máquina ( '\\'>{ fcall skip; } | ^['\\] )+
completa la ejecución cuando ["]
vuelve al estado inicial.
Y finalmente, en el caso de un error de fin de línea con comillas abiertas, squote_err
a dquote_err
y squote_err
para indicar y establecer el código de error correspondiente.
action dquote_err { ret = -1; errsv = BUILDARGV_EDQUOTE; } action squote_err { ret = -1; errsv = BUILDARGV_ESQUOTE; }
La generación de código se lleva a cabo mediante el comando:
ragel -e -L -F0 -o buildargv.c buildargv.rl
Se puede encontrar una lista de líneas de prueba en test_cmdline.c
.
Conclusión
El problema está resuelto.
¿Fue más rápido? Lo dudo ¿Más claro? Si solo eres un experto en Ragel.
No pretendo ser absolutista, agradeceré los comentarios constructivos sobre el código Ragel.
Lista de materiales:
[^ 1]: Adrian Thurston. Ragel State Machine Compiler .