Regex для разбора объявлений функций C/C++

Мне нужно разобрать и разделить функции C и C++ на основные компоненты (тип возвращаемого значения, имя/класс и метод функции, параметры и т. д.).

Я работаю либо с заголовками, либо со списком, где подписи имеют форму:

общественность: недействительным __thiscall myClass::method(int, class myOtherClass *)

У меня есть следующее регулярное выражение, которое работает для большинства функций:

(?<expo>public\:|protected\:|private\:) (?<ret>(const )*(void|int|unsigned int|long|unsigned long|float|double|(class .*)|(enum .*))) (?<decl>__thiscall|__cdecl|__stdcall|__fastcall|__clrcall) (?<ns>.*)\:\:(?<class>(.*)((<.*>)*))\:\:(?<method>(.*)((<.*>)*))\((?<params>((.*(<.*>)?)(,)?)*)\)

Есть несколько функций, которые он не любит анализировать, но, похоже, соответствует шаблону. Я не беспокоюсь о сопоставлении функций, которые в данный момент не являются членами класса (с этим я справлюсь позже). Выражение используется в программе C#, поэтому <label> используются для простого извлечения групп.

Мне интересно, есть ли стандартное регулярное выражение для анализа всех функций или как улучшить мое для обработки странных исключений?


person ssube    schedule 04.08.2010    source источник
comment
Что именно вам нужно для разбора? Похоже, вы анализируете объявления функций, а не сами функции. Это стандартный синтаксис C и C++ или какая-то другая форма?   -  person David Thornley    schedule 04.08.2010
comment
Это объявления функций, я опечатался в заголовке.   -  person ssube    schedule 04.08.2010
comment
Есть инструменты командной строки, такие как ctag, которые делают эту работу. Например: ctags --c++-kinds=f -x myfile.h   -  person karlphillip    schedule 04.08.2010
comment
Компиляторы 101: C++ не является обычным языком, его даже нельзя представить с помощью контекстно-свободной грамматики. Вы не можете разобрать его, используя регулярное выражение, даже регулярные выражения на основе Perl на стероидах.   -  person Juliano    schedule 05.08.2010
comment
@Juliano: C ++ сам по себе может не быть обычным языком, но объявления функций, безусловно, следуют некоторым довольно регулярным шаблонам. Меня не волнует парсинг тел или языка в целом, просто объявления.   -  person ssube    schedule 05.08.2010
comment
@karlphillip: Я попробовал SWIG, кратко, но это не сработало. ctag похоже, что это может быть то, что я ищу, я посмотрю, сработает ли это.   -  person ssube    schedule 05.08.2010
comment
Имейте в виду, что функции не обязательно должны иметь предопределенные типы в качестве возвращаемых типов. Даже в C любой идентификатор может быть возвращаемым типом (например, typedef). FWIW, я никогда не видел, чтобы имена классов начинались с class. На первый взгляд, это, вероятно, будет законным, но вы этого не увидите.   -  person David Thornley    schedule 05.08.2010
comment
@ Дэвид: Верно. Ваш первый пункт, о котором я забыл, может объяснить, почему некоторые функции были упущены. Что касается class в типе, это было бы недопустимо, но на самом деле это не из заголовков, а из списка функций, выплевываемых другой программой (который указывает на классы, перечисления и т. д., добавляя к ним соответствующее ключевое слово). Я их потом выкину.   -  person ssube    schedule 05.08.2010
comment
@peachykeen: объявления функций зависят от типов, которые определяются динамически на этапе синтаксического анализа с помощью перечислений, структур, классов и определений типов. Уже одно это делает его контекстно-зависимой грамматикой. Добавьте к этому шаблону определения и посмотрите, что вы получите.   -  person Juliano    schedule 05.08.2010
comment
@peachykeen: тот факт, что функция может иметь произвольное количество аргументов, означает, что объявления не образуют обычный язык.   -  person Oliver Charlesworth    schedule 05.08.2010
comment
@Oli: Объявления функций не образуют обычный язык по нескольким причинам, но произвольное количество аргументов не является одной из них. Это легко сделать с помощью застежки Клини.   -  person Juliano    schedule 05.08.2010
comment
Допустим, я решил отказаться от обработки заголовков и просто использовать списки, созданные моей другой программой (которая распечатывает экспорт DLL). Все экспортные списки имеют форму, которую я указал в вопросе. Определения типов заменяются их фактическим типом, классы/структуры/перечисления всегда имеют префикс как таковой, шаблоны всегда задаются явно и т. д. Теперь рассматривайте все параметры как единое целое (все, что находится в круглых скобках, является списком параметров). Станет ли это тогда достаточно регулярным, чтобы его можно было проанализировать одним выражением? Я стараюсь не выполнять синтаксический анализ вручную, поэтому мне нужно немного подправить некоторые из них.   -  person ssube    schedule 06.08.2010


Ответы (3)


Общеизвестно, что C++ сложно анализировать; невозможно написать регулярное выражение, которое улавливает все случаи. Например, может быть неограниченное количество вложенных скобок, что показывает, что даже это подмножество языка C++ не является правильным.

Но похоже, что вы стремитесь к практичности, а не к теоретической правильности. Просто продолжайте улучшать регулярное выражение до тех пор, пока оно не обнаружит нужные случаи, и постарайтесь сделать его как можно более строгим, чтобы не было ложных совпадений.

Не зная «странных исключений», которые он не перехватывает, трудно сказать, как улучшить регулярное выражение.

person Thomas    schedule 04.08.2010
comment
С другой стороны, популярные реализации регулярных выражений прекрасно подходят для сопоставления с нерегулярными языками. Например, можно написать регулярное выражение, которое соответствует бесконечным глубоко вложенным парам скобок. - person Tamás Szelei; 29.05.2013

Взгляните на Boost.Spirit, это библиотека повышения, которая позволяет реализовать парсеры рекурсивного спуска. используя только код C++ и без препроцессоров. Вы должны указать BNF Grammar, а затем передать строку для его разобрать. Вы даже можете создать абстрактное синтаксическое дерево (AST), которое полезно для обработки проанализированных данных.

Спецификация BNF выглядит следующим образом: список разделенных целых чисел или слов может выглядеть так:

using spirit::alpha_p;
using spirit::digit_p;
using spirit::anychar_p;
using spirit::end_p;
using spirit::space_p;

// Inside the definition...
integer    = +digit_p;                      // One or more digits.
word       = +alpha_p;                      // One or more letters.
token      = integer | word;                // An integer or a word.
token_list = token >> *(+space_p >> token)  // A token, followed by 0 or more tokens.

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

person jbernadas    schedule 04.08.2010

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

Если вы действительно ограничиваете себя вещами, очень близкими к вашему примеру (ровно 2 аргумента и т. д.), то не могли бы вы привести пример чего-то, что не соответствует?

person Oliver Charlesworth    schedule 04.08.2010
comment
Вариант регулярных выражений .NET — пока единственный вариант, который может иметь произвольные уровни вложенности. Но использовать его для такой работы я бы не стал. - person Abel; 04.08.2010
comment
@Abel: вариант регулярного выражения .Net является рекурсивным и выполняет откат (таким образом, это не DFA). Это было скопировано с Perl, который существует за много-много лет до изобретения .Net. - person Juliano; 05.08.2010
comment
@ Джулиано, не уверен, о чем ты. Действительно, и Perl, и .NET являются NFA, как и большинство разновидностей регулярных выражений. .NET никогда не скрывал, что он точно следует синтаксису Perl. Но .NET представила группы балансировки, что я и имел в виду под произвольные уровни вложенности. Он отслеживает количество открытых и закрытых скобок (или чего-то еще) и успешно находит совпадение только в том случае, если открывающая и закрывающая пары имеют одинаковое количество и являются парными. На данный момент, .NET — единственная версия, которая поддерживает это. - person Abel; 05.08.2010
comment
Это не имеет ничего общего с NFA/DFA. Даже NFA может распознавать только обычный язык, и NFA можно преобразовать в DFA (хотя количество состояний может расти экспоненциально). Вместо этого регулярные выражения Perl/.NET имеют ограниченную поддержку контекстно-свободного разбора языка, что соответствует автомату pushdown. - person Arne Vogel; 16.03.2018