Разбор неправильных прототипов с ++ - PullRequest
3 голосов
/ 08 декабря 2011

Я пытаюсь создать программу, которая анализирует и перечисляет содержимое заголовочных файлов.Пока все хорошо, мне было легко разбирать и перечислять заголовки, которые я написал, но когда я начал разбирать кроссплатформенные заголовки 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 являются макросами, поэтому их можно просто игнорировать, это путь?Естественно, это всего лишь один из множества вариантов нарушений, которые парсер может наткнуться на синтаксический анализ различных заголовков, поэтому приветствуются любые общие методы обработки синтаксического анализа любого «легального» содержимого заголовка.

Ответы [ 2 ]

1 голос
/ 08 декабря 2011

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

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

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

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

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

0 голосов
/ 08 декабря 2011

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

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

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

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

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

...