Como posso analisar um arquivo de cabeçalho C com Perl?
-
13-09-2019 - |
Pergunta
Eu tenho um arquivo de cabeçalho no qual há uma grande estrutura. Eu preciso ler esta estrutura usando algum programa e fazer algumas operações em cada membro da estrutura e escrevê-los de volta.
Por exemplo, eu tenho alguma estrutura como
const BYTE Some_Idx[] = {
4,7,10,15,17,19,24,29,
31,32,35,45,49,51,52,54,
55,58,60,64,65,66,67,69,
70,72,76,77,81,82,83,85,
88,93,94,95,97,99,102,103,
105,106,113,115,122,124,125,126,
129,131,137,139,140,149,151,152,
153,155,158,159,160,163,165,169,
174,175,181,182,183,189,190,193,
197,201,204,206,208,210,211,212,
213,214,215,217,218,219,220,223,
225,228,230,234,236,237,240,241,
242,247,249};
Agora, eu preciso ler este e aplicar alguma operação em cada um a variável de membro e criar uma nova estrutura com ordem diferente, algo como:
const BYTE Some_Idx_Mod_mul_2[] = {
8,14,20, ...
...
484,494,498};
Existe alguma biblioteca Perl já está disponível para isso? Se não Perl, outra coisa, como Python também é OK.
Pode alguém por favor ajuda !!!
Solução
Manter seus dados em torno de mentir em um cabeçalho torna mais complicado para chegar a usar outros programas como Perl. Outra abordagem que você pode considerar é manter esses dados em um banco de dados ou outro arquivo e regenerar o seu arquivo de cabeçalho conforme a necessidade, talvez até mesmo como parte de seu sistema de compilação. A razão para isto é que a geração C é muito mais fácil do que analisar C, é trivial para escrever um script que analisa um arquivo de texto e faz um cabeçalho para você, e esse script poderia mesmo ser chamado a partir de seu sistema de compilação.
Assumindo que você quiser manter seus dados em um arquivo de cabeçalho C, você terá uma de duas coisas para resolver este problema:
- um script one-off rápido para analisar exatamente (ou próximo a exatamente) a entrada que você descreve.
- um script geral, bem escrito que pode analisar C arbitrária e trabalho em geral sobre a lotes de diferentes cabeçalhos.
O primeiro caso parece ser mais comum do que o segundo para mim, mas é difícil dizer de sua pergunta se isso é melhor resolvido por um script que precisa para analisar C arbitrária ou um script que precisa analisar este arquivo específico. Para código que funciona no seu caso específico, as seguintes obras para mim sobre sua entrada:
#!/usr/bin/perl -w
use strict;
open FILE, "<header.h" or die $!;
my @file = <FILE>;
close FILE or die $!;
my $in_block = 0;
my $regex = 'Some_Idx\[\]';
my $byte_line = '';
my @byte_entries;
foreach my $line (@file) {
chomp $line;
if ( $line =~ /$regex.*\{(.*)/ ) {
$in_block = 1;
my @digits = @{ match_digits($1) };
push @digits, @byte_entries;
next;
}
if ( $in_block ) {
my @digits = @{ match_digits($line) };
push @byte_entries, @digits;
}
if ( $line =~ /\}/ ) {
$in_block = 0;
}
}
print "const BYTE Some_Idx_Mod_mul_2[] = {\n";
print join ",", map { $_ * 2 } @byte_entries;
print "};\n";
sub match_digits {
my $text = shift;
my @digits;
while ( $text =~ /(\d+),*/g ) {
push @digits, $1;
}
return \@digits;
}
Analisando C arbitrária é um pouco vale a pena complicado e não-lo para muitas aplicações, mas talvez você precisa realmente fazer isso. Um truque é deixar GCC fazer a análise para você e ler na árvore de análise do GCC usando um módulo CPAN chamado GCC :: TranslationUnit . Aqui está o comando GCC para compilar o código, supondo que você tem um único arquivo chamado test.c:
gcc -fdump-tradução-unit -c test.c
Aqui está o código Perl para ler na árvore de análise:
use GCC::TranslationUnit;
# echo '#include <stdio.h>' > stdio.c
# gcc -fdump-translation-unit -c stdio.c
$node = GCC::TranslationUnit::Parser->parsefile('stdio.c.tu')->root;
# list every function/variable name
while($node) {
if($node->isa('GCC::Node::function_decl') or
$node->isa('GCC::Node::var_decl')) {
printf "%s declared in %s\n",
$node->name->identifier, $node->source;
}
} continue {
$node = $node->chain;
}
Outras dicas
Desculpe se isso é uma pergunta estúpida, mas por que se preocupar com a análise do arquivo em tudo? Por que não escrever um programa em C que #includes o cabeçalho, processa-o conforme necessário e, em seguida, cospe para fora a fonte para o cabeçalho modificado. Tenho certeza que isso seria mais simples do que as soluções Perl / Python, e seria muito mais confiável porque o cabeçalho estariam sendo analisados ??pelo analisador compiladores C.
Você realmente não fornecem muita informação sobre como o que deve ser modificado deve ser determinado, mas para resolver o seu exemplo específico:
$ perl -pi.bak -we'if ( /const BYTE Some_Idx/ .. /;/ ) { s/Some_Idx/Some_Idx_Mod_mul_2/g; s/(\d+)/$1 * 2/ge; }' header.h
Quebrando que para baixo, -p diz loop através de arquivos de entrada, colocando cada linha $_
, executar o código fornecido, em seguida, imprimir $_
. -i.bak permite a edição no local, renomear cada arquivo original com um sufixo bak e imprimir em um novo arquivo chamado qualquer que seja o original. -w habilita advertências. -e '....' fornece o código para ser executado para cada linha de entrada. header.h é o único arquivo de entrada.
No código perl, cheques if ( /const BYTE Some_Idx/ .. /;/ )
que estamos em uma série de linhas que começam com um /const BYTE Some_Idx/
linha de correspondência e terminando com um /;/
linha correspondente.
s /.../.../ g faz uma substituição tantas vezes quanto possível. /(\d+)/
corresponde a uma série de dígitos. A / e bandeira diz que o resultado ($1 * 2
) é o código que deve ser avaliada para produzir uma cadeia de substituição, em vez de simplesmente uma seqüência de substituição. $ 1 é os dígitos que devem ser substituídos.
Se tudo que você precisa fazer é modificar estruturas, você pode usar diretamente regex para dividir e aplicar alterações a cada valor na struct, olhando para a declaração eo fim}; saber quando parar.
Se você realmente precisa de uma solução mais geral você poderia usar um gerador de analisador, como pyparsing
Existe um módulo Perl chamado Parse :: RecDescent que é uma muito poderosa gerador de analisador descendente recursivo. Ele vem com um monte de exemplos. Um deles é um gramática que pode analisar C .
Agora, eu não acho que isso é importante no seu caso, mas os analisador sintático descendente recursivo usando Parse :: RecDescent são algoritmos mais lenta (O (n ^ 2), eu acho) do que ferramentas como Parse :: Yapp ou Parse :: EYapp . Eu não tenho verificado se Parse :: EYapp vem com um tal exemplo C-parser, mas se assim for, isso é a ferramenta eu recomendo aprendizagem.
solução Python (não completa, apenas uma sugestão;)) Desculpe se quaisquer erros - não testado
import re
text = open('your file.c').read()
patt = r'(?is)(.*?{)(.*?)(}\s*;)'
m = re.search(patt, text)
g1, g2, g3 = m.group(1), m.group(2), m.group(3)
g2 = [int(i) * 2 for i in g2.split(',')
out = open('your file 2.c', 'w')
out.write(g1, ','.join(g2), g3)
out.close()
Existe um módulo Perl realmente útil chamado Convert :: Binary :: C que os arquivos de cabeçalho parses C e converte estruturas de / para estruturas de dados Perl.
Você pode sempre usar pack
/ unpack
, para ler e escrever os dados.
#! /usr/bin/env perl
use strict;
use warnings;
use autodie;
my @data;
{
open( my $file, '<', 'Some_Idx.bin' );
local $/ = \1; # read one byte at a time
while( my $byte = <$file> ){
push @data, unpack('C',$byte);
}
close( $file );
}
print join(',', @data), "\n";
{
open( my $file, '>', 'Some_Idx_Mod_mul_2.bin' );
# You have two options
for my $byte( @data ){
print $file pack 'C', $byte * 2;
}
# or
print $file pack 'C*', map { $_ * 2 } @data;
close( $file );
}
Para o GCC :: TranslationUnit exemplo ver hparse.pl de http://gist.github.com/395160 que irá fazê-lo em C :: DynaLib, eo Ctypes ainda não escrita também. Este Analisa funções para FFI de e estruturas não descalços contrárias converter :: Binary :: C. hparse só irá adicionar estruturas se usado como args func.