Как я могу проанализировать заголовочный файл C с Perl?

Все это полностью зависит от реализации, которую вы понимаете. Вы не можете рассчитывать ни на что из этого. Здесь нет «правила».

В примере наследования, вот как может выглядеть виртуальная таблица для классов A и B:

      class A
+-----------------+
| pointer to A::v |
+-----------------+

      class B
+-----------------+
| pointer to A::v |
+-----------------+
| pointer to B::w |
+-----------------+

Как видите, если у вас есть указатель на виртуальную таблицу класса B, он также отлично подходит как виртуальная таблица класса А.

В вашем примере с классом C, если вы об этом думаете, нет способа создать виртуальную таблицу, которая была бы действительной как таблица для класса C, класса A и класса B. Таким образом, компилятор создает два. Одна виртуальная таблица действительна для классов A и C (наиболее вероятно), а другая - для классов A и B.

6
задан brian d foy 15 June 2009 в 17:03
поделиться

9 ответов

Хранение ваших данных в заголовке затрудняет использование других программ, таких как Perl. Другой подход, который вы можете рассмотреть, - сохранить эти данные в базе данных или другом файле и при необходимости повторно создать файл заголовка, возможно, даже как часть вашей системы сборки. Причина этого в том, что создание C намного проще, чем синтаксический анализ C, тривиально написать сценарий, который анализирует текстовый файл и создает заголовок для вас, и такой сценарий можно даже вызвать из вашей системы сборки.

Предполагая, что вы хотите сохранить свои данные в заголовочном файле C, для решения этой проблемы вам потребуется одно из двух:

  • быстрый одноразовый сценарий для точного (или близкого к точному) синтаксического анализа вводимых вами описать.
  • общий, хорошо написанный скрипт, который может анализировать произвольный C и работать с множеством различных заголовков.

Первый случай кажется мне более распространенным, чем второй, но из вашего вопроса трудно сказать, лучше ли это решается сценарием, которому нужно анализировать произвольный C, или сценарием, который должен анализировать этот конкретный файл. Для кода, который работает в вашем конкретном случае, у меня работает следующее на вашем вводе:

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

Разбор произвольного C немного сложен и не стоит того для многих приложений, но, возможно, вам действительно нужно это сделать. Один из приемов состоит в том, чтобы позволить GCC выполнить синтаксический анализ за вас и прочитать дерево синтаксического анализа GCC с помощью модуля CPAN с именем GCC :: TranslationUnit . у меня с вашими входными данными работает следующее:

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

Разбор произвольного C немного сложен и не стоит того для многих приложений, но, возможно, вам действительно нужно это сделать. Один из приемов состоит в том, чтобы позволить GCC выполнить синтаксический анализ за вас и прочитать дерево синтаксического анализа GCC с помощью модуля CPAN с именем GCC :: TranslationUnit . у меня с вашими входными данными работает следующее:

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

Разбор произвольного C немного сложен и не стоит того для многих приложений, но, возможно, вам действительно нужно это сделать. Один из приемов состоит в том, чтобы позволить GCC выполнить синтаксический анализ за вас и прочитать дерево синтаксического анализа GCC с помощью модуля CPAN с именем GCC :: TranslationUnit . Вот команда GCC для компиляции кода при условии, что у вас есть единственный файл с именем test.c:

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

Вот код Perl для чтения в дереве синтаксического анализа:

  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;
  }
9
ответ дан 8 December 2019 в 03:40
поделиться

Решение Python (не полное, просто подсказка;)) Извините, если есть ошибки - не проверено

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()
2
ответ дан 8 December 2019 в 03:40
поделиться

Если все, что вам нужно сделать, это изменить структуры, вы можете напрямую использовать регулярное выражение для разделения и применения изменений к каждому значению в структуре, ища объявление и окончание}; чтобы знать, когда остановиться.

Если вам действительно нужно более общее решение, вы можете использовать генератор парсеров, например PyParsing

3
ответ дан 8 December 2019 в 03:40
поделиться

На самом деле вы не предоставляете много информации о том, как следует определить, что следует изменить, но для решения вашего конкретного примера:

$ 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

Разбивая это, -p указывает цикл по входным файлам, помещая каждую строку в $ _ , выполняя предоставленный код , затем печать $ _ . -i.bak позволяет редактировать на месте, переименовывая каждый исходный файл с суффиксом .bak и печатая в новый файл с именем, которое было у оригинала. -w включает предупреждения. -e '....' предоставляет код для запуска для каждой строки ввода. header.h - единственный входной файл.

В коде perl if (/ const BYTE Some_Idx / .. /; /) проверяет, что мы находимся в диапазоне строк, начинающихся со строки, соответствующей / const BYTE Some_Idx / и заканчивается строкой, соответствующей /; / . s /.../.../ g выполняет замену столько раз, сколько возможно. / (\ d +) / соответствует серии цифр. Флаг / e указывает, что результат ( $ 1 * 2 ) - это код, который должен быть вычислен для создания строки замены, а не просто строки замены. $ 1 - это цифры, которые необходимо заменить.

4
ответ дан 8 December 2019 в 03:40
поделиться

Простите, если это глупый вопрос, но зачем вообще беспокоиться о разборе файла? Почему бы не написать программу на C, которая # включает заголовок, обрабатывает его по мере необходимости, а затем выводит исходный код для измененного заголовка. Я уверен, что это будет проще, чем решения Perl / Python, и будет намного надежнее, потому что заголовок будет анализироваться синтаксическим анализатором компилятора C.

6
ответ дан 8 December 2019 в 03:40
поделиться

Существует модуль Perl под названием Parse :: RecDescent , который является очень мощным генератором рекурсивного синтаксического анализатора спуска. Там есть множество примеров. Одна из них - грамматика , которая может анализировать C .

Я не думаю, что это имеет значение в вашем случае, но парсеры рекурсивного спуска, использующие Parse :: RecDescent, алгоритмически медленнее (O (n ^ 2), я думаю), чем инструменты вроде Parse :: Yapp или Parse :: EYapp . Я не проверял, поставляется ли Parse :: EYapp с таким примером C-парсера, но если да, то я бы рекомендовал изучить этот инструмент.

2
ответ дан 8 December 2019 в 03:40
поделиться

Существует действительно полезный модуль Perl под названием Convert :: Binary :: C , который анализирует файлы заголовков C и преобразует структуры из / в структуры данных Perl.

2
ответ дан 8 December 2019 в 03:40
поделиться

Вы всегда можете использовать pack / unpack для чтения и записи данных.

#! /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
ответ дан 8 December 2019 в 03:40
поделиться

Для примера GCC :: TranslationUnit см. Hparse.pl из http://gist.github.com/395160 , который превратится в C :: DynaLib, а также в еще не написанные Ctypes. Это анализирует функции для FFI, а не простые структуры, противоречащие Convert :: Binary :: C. hparse добавит структуры, только если они используются как аргументы функции.

0
ответ дан 8 December 2019 в 03:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: