Разбор нестандартных прототипов C++

Я пытаюсь создать программу, которая анализирует и перечисляет содержимое файлов заголовков. До сих пор все было хорошо, мне было легко анализировать и перечислять заголовки, которые я написал, но когда я начал анализировать заголовки кроссплатформенного API, все стало беспорядочно.

Мой текущий подход довольно упрощен, вот пример псевдокода разбора следующей функции:

void foo(int a);

void is a type, so we are dealing with instancing a type
foo is the name of that type
foo is followed by brackets, meaning it is a function of type void named foo
   int is a type...
   a is the name of that type instance
foo is a function of type void that takes one parameter of type int named a

Однако, когда я занялся более крупными и сложными заголовками, я наткнулся на несколько неправильные прототипы, включающие макросы и черт знает что. Пример:

GLAPI void APIENTRY glEvalCoord1d( GLdouble u );

GLAPI и APIENTRY являются макросами, зависящими от платформы. Что портит мою простую схему синтаксического анализа, поскольку она ожидает, что имя объекта будет соответствовать его типу. Эти два макроса транслируются в __stdcall, __declspec(dllimport) или extern, но теоретически они могут означать что угодно, и их значение остается неясным до момента компиляции.

Как написать свой парсер, чтобы он справлялся с такими сценариями и не запутался? Сами макросы определяются на более ранней стадии, поэтому синтаксический анализатор может знать, что GLAPI и APIENTRY являются макросами, поэтому их можно просто игнорировать, так ли это? Естественно, это всего лишь одна из многих вариаций нарушений, с которыми синтаксический анализатор может столкнуться при анализе различных заголовков, поэтому приветствуются любые общие методы обработки любого «легального» содержимого заголовков.


person dtech    schedule 08.12.2011    source источник
comment
Нет, мне не нужен совет. Привет, однако.   -  person Lightness Races in Orbit    schedule 08.12.2011
comment
Прошу прощения, был восклицательный знак Нужен совет! но по какой-то причине сайт изменил его на ? и даже после того, как я отредактировал свой пост, он все еще ставит вопросительный знак ... Странно, но я полагаю, что, поскольку я публикую вопрос, вопросительный знак стоит по умолчанию ... Дайте угадаю, вы пытались его отредактировать и тоже не удалось ?? ? :)   -  person dtech    schedule 08.12.2011
comment
Ха, ты прав... кажется, что он переписывает восклицательные знаки.   -  person Lightness Races in Orbit    schedule 08.12.2011
comment
мета. stackexchange.com/questions/115327/   -  person Lightness Races in Orbit    schedule 08.12.2011
comment
Вы понимаете, что ваша программа может вызывать препроцессор, верно?   -  person asveikau    schedule 08.12.2011


Ответы (2)


Нет никакой реальной альтернативы расширению макросов перед синтаксическим анализом, по крайней мере, если вы хотите обрабатывать файлы заголовков с такой же сложностью, как у Microsoft, или любые другие файлы заголовков, связанные с системой компиляции, которая существует уже 10 или более лет.

Необработанный исходный код НЕ C; это просто необработанный исходный код. Макросы (и условные операторы препроцессора, о которых вы, к удивлению, не упомянули) могут редактировать исходный код не произвольным, а впечатляюще сложным образом. И вы часто не можете знать, какие макросы используются или расширяются условные операторы, если вы также не обрабатываете #includes.

Вы можете заставить GCC выполнить расширение препроцессора за вас, а затем проанализировать его. Это был бы самый простой способ приблизиться к этому.

При этом остается проблема синтаксического анализа реального кода C со всеми сложностями деклараторов и двусмысленностью фрагментов, таких как TX;, где значение оператора зависит от объявления T. Для точного анализа заголовков , вам нужен полный C-парсер.

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

person Ira Baxter    schedule 08.12.2011

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

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

Ключевые слова, зависящие от платформы, такие как __declspec и __attribute__, имеют очень ограниченный синтаксис, и их всего несколько, поэтому их можно специально удалить.

Вы можете посмотреть, как doxygen справляется с этим, потому что он делает почти то, что вам нужно, и обрабатывает макросы. Это позволяет расширить список макросов, как определено, и тех, которые должны быть расширены до пользовательского значения. Вы можете адаптировать это, чтобы расширить __declspec(x) до нуля и расширить все остальные до их определенного значения по умолчанию.

Это, конечно, не надежно, но поиск и замена — это самое простое функциональное решение, которое вы получите. Вам нужно следовать стандартным правилам препроцессора C++, которые не очень сложны, с дополнительными макросами (const, declspec и т. д.), чтобы удалить дополнительные атрибуты и проанализировать окончательные результаты.

person ssube    schedule 08.12.2011