Uso divertido do Ragel State Machine Compiler para criar uma função de análise de linha em int argc, char * argv [].
Tudo começou com o fato de que a função buildargv era necessária para analisar a cadeia de caracteres para transferência subsequente para
int main (int argc, char *argv[]) { body }
Bem, pensei, não pode ser impossível emprestar para lugar algum, agora encontramos ... E não encontrei ...

Bem, não que eu não tivesse encontrado, por exemplo, https://github.com/gcc-mirror/gcc/blob/master/libiberty/argv.c (a GPLv2 é sempre boa), assumo imediatamente tais obrigações não estava pronto. Definitivamente, existe essa função no bash (a GPLv3 é ainda melhor). zsh? - vá encontrar (eu encontrei ... - eu não quero).
Em geral, não encontrei o que queria, mas não gostei do que encontrei. Bem, no final, tenho o direito a isso, mesmo assim, faço para mim uma sede de entretenimento no processo.
Eu não queria escrever esse caso de maneira convencional a partir da palavra, fiquei até chateado por esse motivo.
Em geral, encontramos o Ragel State Machine Compiler.
Toolkit
- gcc;)
- ragel
- fazer
- lcov
- libcheck
O projeto pode ser encontrado aqui: JOYFUL CMDLINE PARSER ESCRITO EM RAGEL
Declaração do problema
Na entrada, temos uma string de qualquer tipo, a tarefa é obter da string uma matriz de argumentos separados por um espaço ou tabulação, com:
- Qualquer caractere após o caractere de escape
\
deve ser ignorado. - Qualquer caractere entre duas dobras ou deve
ser considerado um elemento - No caso de
'
ou '
não fechado, um erro será retornado
Em geral, não há muitas condições. E Ragel é bastante adequado para esta tarefa.
Implementação explicada
Vamos declarar uma máquina com o nome "buildargv" e pedir ao Ragel para colocar seus dados no início do arquivo (5.8.1 Write Data).
%%{ machine buildargv; write data; }%%
Em seguida, declaramos uma máquina lineElement
, que por sua vez consiste em combinar (2.5.1 Union) duas máquinas: arg
e whitespace
.
lineElement = arg >start_arg %end_arg | whitespace; main := blineElements**;
Na entrada e na saída da máquina arg
, as ações start_arg
e 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_++; }
Além disso, a tarefa start_arg
para salvar a posição do caractere na entrada e a tarefa end_arg
adicionam um novo elemento ao array argv
, no caso de uma saída bem-sucedida da máquina arg
.
Agora vamos dar uma olhada em arg
.
arg = '\''> { fcall squote; } | '"'>{ fcall dquote; } | ( '\\'>{fcall skip;} | ^[ \t"'\\] )+;
Consiste em uma união de três máquinas '
, "
e (\ | ^[ \t"'\])
, a última por sua vez é uma união de \
e ^[ \t"'\]
respectivamente.
Quando encontramos o caractere '
chamamos squote
'
, chamamos squote
, ou se o caractere atual é \
chamamos skip
, que ignora qualquer caractere que o segue e qualquer caractere não é 0x20
(espaço), 0x09
(tab), '
, "
ou \
é considerado correto .
Resta considerar uma parte muito pequena:
skip := any @{ fret; }; dquote := ( '\\'>{ fcall skip; } | ^[\\] )+ :> ["] @{ fret; } @err(dquote_err); squote := ( '\\'>{ fcall skip; } | ^[\\] )+ :> ['] @{ fret; } @err(squote_err);
Com o skip
já descobrimos o que ^['\\]
também não deve causar perguntas. E aqui :>
esta é Entry-Guarded Concatenation
(4.2 operadores protegidos que encapsulam prioridades), seu significado é que a máquina ( '\\'>{ fcall skip; } | ^['\\] )+
completa a execução quando ["]
retorna ao estado inicial.
E, finalmente, no caso de um erro de fim de linha com aspas em aberto, dquote_err
e squote_err
para indicar e definir o código de erro correspondente.
action dquote_err { ret = -1; errsv = BUILDARGV_EDQUOTE; } action squote_err { ret = -1; errsv = BUILDARGV_ESQUOTE; }
A geração de código é realizada pelo comando:
ragel -e -L -F0 -o buildargv.c buildargv.rl
Uma lista de linhas de teste pode ser encontrada em test_cmdline.c
.
Conclusão
O problema está resolvido.
Foi mais rápido? Eu duvido. Mais claro? Se você é um especialista em Ragel.
Não finjo o absolutismo, serei grato por comentários construtivos sobre o código de Ragel.
Lista de materiais:
[^ 1]: Adrian Thurston. Ragel State Machine Compiler .