pycparser ParseError с typedef
Я использую pycparser для синтаксического анализа кода C, который не могу скомпилировать с помощью cpp перед анализом, поэтому я вручную удаляю все комментарии и директивы препроцессора с помощью следующей функции:
def remove_comments(text):
def replacer(match):
s = match.group(0)
if s.startswith('/') or s.startswith('#'):
return ""
else:
return s
pattern = re.compile(
r'#.*?$|//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
re.DOTALL | re.MULTILINE
)
return re.sub(pattern, replacer, text)
Это вывод этой функции из файла memmgr.c из примеров:
typedef ulong Align;
union mem_header_union
{
struct
{
union mem_header_union* next;
ulong size;
} s;
Align align_dummy;
};
typedef union mem_header_union mem_header_t;
static mem_header_t base;
static mem_header_t* freep = 0;
static byte pool[POOL_SIZE] = {0};
static ulong pool_free_pos = 0;
void memmgr_init()
{
base.s.next = 0;
base.s.size = 0;
freep = 0;
pool_free_pos = 0;
}
static mem_header_t* get_mem_from_pool(ulong nquantas)
{
ulong total_req_size;
mem_header_t* h;
if (nquantas < MIN_POOL_ALLOC_QUANTAS)
nquantas = MIN_POOL_ALLOC_QUANTAS;
total_req_size = nquantas * sizeof(mem_header_t);
if (pool_free_pos + total_req_size <= POOL_SIZE)
{
h = (mem_header_t*) (pool + pool_free_pos);
h->s.size = nquantas;
memmgr_free((void*) (h + 1));
pool_free_pos += total_req_size;
}
else
{
return 0;
}
return freep;
}
void* memmgr_alloc(ulong nbytes)
{
mem_header_t* p;
mem_header_t* prevp;
ulong nquantas = (nbytes + sizeof(mem_header_t) - 1) / sizeof(mem_header_t) + 1;
if ((prevp = freep) == 0)
{
base.s.next = freep = prevp = &base;
base.s.size = 0;
}
for (p = prevp->s.next; ; prevp = p, p = p->s.next)
{
if (p->s.size >= nquantas)
{
if (p->s.size == nquantas)
{
prevp->s.next = p->s.next;
}
else
{
p->s.size -= nquantas;
p += p->s.size;
p->s.size = nquantas;
}
freep = prevp;
return (void*) (p + 1);
}
else if (p == freep)
{
if ((p = get_mem_from_pool(nquantas)) == 0)
{
printf("!! Memory allocation failed !!\n");
return 0;
}
}
}
}
Но я получаю это ParseError:
pycparser.plyparser.ParseError: :1:15: before: Align
Что не так с pycparser?
2 ответа
Я полагаю, что был typedef unsigned long ulong;
в каком-то включенном файле. Без этого заявления ulong
не может появиться там, где грамматика требует имени типа.
Попробуйте добавить объявление ulong
где-то до его первого использования.
Чтобы более конкретно о вопросе: "Что не так с pycparser?":
Цель pycparser - анализ программ на C. Это не приблизительный парсер; фактически он нацелен на создание полного и точного анализа любой действительной программы C99.
К сожалению, невозможно точно проанализировать программу на C, не зная, какие идентификаторы являются типичными. Нет необходимости знать точный тип идентификатора, поэтому pycparser не требует доступа ко всем прототипам и глобальным определениям; однако он требует доступа ко всем соответствующим typedef
s.
Это описано в разделе 3.2 файла readme для pycparser, который указывает на более длительное обсуждение на веб-сайте автора:
Ключевой момент, который необходимо понять, заключается в том, что pycparser на самом деле не заботится о семантике типов. Нужно только знать, относится ли какой-либо токен, встречающийся в источнике, к ранее определенному типу. Это важно для правильного анализа C.
По предложению Eli, вам лучше всего собрать только определения типов, используемые кодом, который вы хотите проанализировать, и вставить их в начале кода. Вероятно, их не так уж много.
Эссе Эли Бендерского отлично, и его стоит прочитать. Позвольте мне привести пару примеров кода на C, который нельзя проанализировать, не зная, является ли имя typedef или нет.
Классический пример, я думаю, хорошо известен:
(foo) - 3 * 4
Это выражение имеет два возможных анализа, только один из которых может применяться в любой данной программе. Слева разбор если foo
переменная; справа разбор если foo
это тип:
- *
/ \ / \
/ \ / \
foo * cast 4
/ \ / \
/ \ / \
3 4 foo -
|
|
3
Другими словами, если foo
является переменной, выражение вычитает 3*4
от foo
, Но если foo
это тип, выражение бросает -3
печатать foo
а затем умножает результат на 4`.
Очевидно, что конкретное приложение, из которого был составлен этот вопрос, на самом деле не требует детальных знаний о разборе каждого выражения. Но нет способа сообщить об этом факте Pycparser; Pycparser's предназначен для обеспечения полного анализа.
В любом случае, возможно построить более подходящий пример. Рассмотрим два утверждения (которые не могут появляться в одной и той же единице перевода):
foo (*bar()) ();
а также
foo (*bar()) ();
Несмотря на их сходство (:-)), эти два утверждения совершенно разные. Первый объявляет функцию с именем bar
, Второй вызывает функцию с именем foo
после вызова функции с именем bar
вычислить аргумент foo
,
Даже если вы просто собираете декларации, важно знать, было ли это заявление декларацией или нет. (Да, эта конструкция очень редкая; она может не появиться нигде в анализируемой базе кода. Но как это узнает pycparser?)
Вот полные контексты:
#include <stdio.h> | #include <stdio.h>
typedef int foo; |
int answer() { return 42; } | int answer() { return 42; }
|
| int (*foo(int a)) () {
| printf("%d\n", a);
| return answer;
| }
|
| static const int unused = 43;
int (*bar()) () { return answer; } | int const* bar() { return &unused; }
|
int main() { | int main() {
/* Declare bar */ | /* Call foo */
foo (*bar()) (); | foo (*bar()) ();
printf("%d\n", bar()()); | return 0;
return 0; | }
} |
Препроцессор C не только удаляет комментарии. Он также обрабатывает все макросы, в том числе #include
, который тянет в заголовочных файлах. В вашем случае он пытается #include "memmgr.h"
, который имеет это typedef
:
typedef unsigned long ulong;
Наряду с некоторыми другими.
Суть в том, что вы должны вызывать препроцессор перед pycparser. Если у вас есть действительный C-код, нет никаких причин, по которым препроцессор не должен работать. Это может быть тема для отдельного вопроса SO в C
тег.