2009-06-15 13 views
6

Ho un file di intestazione in cui è presente una struttura di grandi dimensioni. Ho bisogno di leggere questa struttura usando un programma e fare alcune operazioni su ciascun membro della struttura e scriverle.Come posso analizzare un file di intestazione C con Perl?

Per esempio io ho un po 'di struttura come

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}; 

Ora, ho bisogno di leggere questo e applicare un po' il funzionamento su ciascuna delle variabile membro e creare una nuova struttura con ordine diverso, qualcosa di simile:

const BYTE Some_Idx_Mod_mul_2[] = { 
8,14,20, ... 
... 
484,494,498}; 

Esiste già una libreria Perl disponibile per questo? Se non è Perl, anche qualcos'altro come Python è OK.

Qualcuno può aiutare, per favore !!!

+1

Un motivo particolare per non utilizzare C? Penserei che sia un modo logico per affrontare questo problema. –

+0

stai leggendo e modificando un'intestazione C? Perché? –

+0

@Ryan, puoi essere un po 'più elaborato. Stai dicendo che posso usare lo stesso file di intestazione da C, e quindi usarlo per l'analisi? @Carson, in realtà, ho appena dato un esempio, ho bisogno di fare cose come la divisione, la fusione e cose del genere. – Alphaneo

risposta

9

Tenere i dati in giro in un'intestazione rende più complicato ottenere l'utilizzo di altri programmi come Perl. Un altro approccio che potreste considerare è quello di mantenere questi dati in un database o in un altro file e rigenerare il file di intestazione quando necessario, magari anche come parte del vostro sistema di compilazione. La ragione di ciò è che generare C è molto più facile dell'analisi di C, è banale scrivere uno script che analizza un file di testo e crea un'intestazione per te, e tale script potrebbe anche essere invocato dal tuo sistema di compilazione.

Supponendo che si desidera conservare i dati in un file di intestazione C, avrete bisogno di una delle due cose per risolvere questo problema:

  • un rapido uno-off di script per analizzare esattamente (o quasi esattamente) l'input che descrivi.
  • uno script generico e ben scritto che può analizzare la C arbitraria e lavorare in genere su molte intestazioni diverse.

Il primo caso sembra più comune del secondo per me, ma è difficile capire dalla domanda se questo è risolto meglio da uno script che deve analizzare C arbitrario o uno script che deve analizzare questo specifico file . Per il codice che funziona sul vostro caso specifico, i seguenti lavori per me sul vostro ingresso:

#!/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; 
} 

Analisi arbitraria C è un po 'complicato e non vale la pena per molte applicazioni, ma forse è necessario fare in realtà questo. Un trucco è lasciare che GCC esegua l'analisi per te e legga l'albero di analisi di GCC usando un modulo CPAN chiamato GCC::TranslationUnit. Ecco il comando GCC per compilare il codice, a patto di avere un singolo file denominato test.c:

gcc -fdump-translation-unit test.c -c

Ecco il codice Perl per leggere nel parse albero:

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; 
    } 
3

Se tutto ciò che devi fare è modificare le strutture, puoi usare direttamente regex per dividere e applicare le modifiche a ogni valore nella struct, cercando la dichiarazione e il finale}; per sapere quando fermarsi.

Se davvero bisogno di una soluzione più generale si può utilizzare un generatore di parser, come PyParsing

4

non si ha realmente fornire molte informazioni su come ciò che deve essere modificato deve essere determinato, ma per affrontare il tuo esempio specifico :

$ 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 

rottura che verso il basso, -p dice ciclo attraverso i file di input, mettendo ogni riga $_, l'esecuzione del codice in dotazione, quindi stampare $_. -i.bak abilita la modifica sul posto, rinomina ogni file originale con un suffisso .bak e stampa su un nuovo file chiamato qualunque sia l'originale. -w abilita gli avvisi. -e '....' fornisce il codice da eseguire per ogni riga di input. header.h è l'unico file di input.

Nel codice perl, if (/const BYTE Some_Idx/ .. /;/) controlla che siamo in un intervallo di righe che iniziano con una riga che corrisponde a /const BYTE Some_Idx/ e termina con una riga corrispondente a /;/. s /.../.../ g fa una sostituzione il più volte possibile. /(\d+)/ corrisponde a una serie di cifre. Il flag/e dice che il risultato ($1 * 2) è un codice che dovrebbe essere valutato per produrre una stringa sostitutiva, invece di una semplice stringa di sostituzione. $ 1 sono le cifre che dovrebbero essere sostituite.

2

C'è un modulo Perl chiamato Parse::RecDescent, che è un potente generatore di parser ricorsivo discesa. Viene fornito con un sacco di esempi. Uno di questi è uno grammar that can parse C.

Ora, io non credo che questo le cose nel tuo caso, ma i parser discesa ricorsiva usando Parse :: RecDescent sono algoritmicamente più lento (O (n^2), credo) di strumenti come Parse::Yapp o Parse::EYapp. Non ho controllato se Parse :: EYapp abbia un tale esempio di parser C, ma se è così, questo è lo strumento che consiglierei di imparare.

2

soluzione Python (non completa, solo un accenno;)) Scusate se eventuali errori - non testati

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() 
6

Scusate se questa è una domanda stupida, ma perché si preoccupano per l'analisi del file a tutti? Perché non scrivere un programma in C che # include l'intestazione, lo elabora come richiesto e quindi sputa la sorgente per l'intestazione modificata. Sono sicuro che sarebbe più semplice delle soluzioni Perl/Python, e sarebbe molto più affidabile perché l'intestazione sarebbe stata analizzata dal parser dei compilatori C.

+0

@Neil, i dati del file di intestazione verranno infine trasformati in dati ROM per l'hardware. Ciò richiede un formato specifico, che si adatti alla generazione della tabella ROM. E ci sono molti file header con nomi di variabili diversi, e non sono sicuro che possano essere inclusi in fase di esecuzione. – Alphaneo

+0

Non intendo includere l'intestazione nel tuo programma reale, voglio dire scrivere UN ALTRO programma per elaborare l'intestazione, ma scrivere quel programma in C non Perl o Python. –

+0

@Neil, penso che la tua idea sarà buona se abbiamo alcuni file h (eader). Ma quello che abbiamo qui è circa 100+ file di intestazione, ognuno con una media di circa 10 diverse definizioni di datastructure. Abbiamo pensato, includendo tutti questi in un file C, sarà un grande compito. La formula di analisi che stiamo pianificando di applicare è la stessa per tutti i file, quindi abbiamo pensato che l'analisi (potrebbe) essere una buona idea. Ma poi, lo implementeremo usando C se nessun altro metodo funziona. – Alphaneo

2

C'è un modulo Perl molto utile chiamato Convert::Binary::C che analizza i file di intestazione C e converte le strutture da/a strutture di dati Perl.

0

È sempre possibile utilizzare pack/unpack, leggere e scrivere i dati.

#! /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); 
} 
0

Per l'esempio GCC :: TranslationUnit vedere hparse.pl da http://gist.github.com/395160 che renderà in C :: DynaLib, e il ctypes non ancora scritto anche. Questo analizza le funzioni per le FFI e non per le strutture nude contrarie a Convert :: Binary :: C. hparse aggiungerà solo structs se usato come func args.

Problemi correlati