C++: синтаксический анализ с помощью простого регулярного выражения или использовать sscanf?

Мне нужно разобрать строку типа func1(arg1, arg2); func2(arg3, arg4);. Это не очень сложная проблема синтаксического анализа, поэтому я бы предпочел не прибегать к flex/bison или подобным утилитам.

Мой первый подход состоял в том, чтобы попытаться использовать реализацию POSIX C regcomp/regexec или Boost C++ std::regex. Я написал следующее регулярное выражение, которое не работает (далее объясню почему).

 "^"
 "[ ;\t\n]*"
 "(" // (1) identifier
    "[a-zA-Z_][a-zA-Z0-9_]*"
 ")"
 "[ \t\n]*"
 "(" // (2) non-marking
    "\["
    "(" // (3) non-marking
       "[ \t]*"
       "(" // (4..n-1) argument
          "[a-zA-Z0-9_]+"
       ")"
       "[ \t\n]*"
       ","
    ")*"
    "[ \t\n]*"
    "(" // (n) last argument
       "[a-zA-Z0-9_]+"
    ")"
    "]"
 ")?"
 "[ \t\n]*"
 ";"

Обратите внимание, что группа 1 захватывает идентификатор, а группы 4..n-1 предназначены для захвата аргументов, кроме последнего, который захватывается группой n.

Когда я применяю это регулярное выражение, скажем, к func(arg1, arg2, arg3), результат, который я получаю, представляет собой массив {func, arg2, arg3}. Это неправильно, потому что arg1 в нем нет!

Проблема в том, что в стандартных библиотеках регулярных выражений подмаркировки фиксируют только последнее совпадение. Другими словами, если вы, например, применили регулярное выражение "((a*|b*))*" к "babb", результаты внутреннего совпадения будут bb, а все предыдущие захваты будут забыты.

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

Так что я не знаю, не упустил ли я что-то здесь... В этом случае я должен использовать вместо этого sscanf или что-то подобное?

Обратите внимание, что я предпочитаю использовать стандартные библиотеки C/C++ (и, возможно, повысить).


person Giovanni Funchal    schedule 19.04.2010    source источник


Ответы (3)


Если вы хотите использовать Regex, было бы проще разделить его на 2 этапа. На шаге 1 вы найдете

func1(stuff);

и превратите его в func1 и stuff

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

person Rik Heywood    schedule 19.04.2010

Как насчет Boost Spirit?

person Fred Larson    schedule 19.04.2010
comment
Я нахожу Boost::Spirit немного запутанным... Есть также Google RE2. - person Giovanni Funchal; 19.04.2010
comment
Ну, я нахожу RE грязными. Каждому свое. 8м) - person Fred Larson; 19.04.2010

Если бы это был Ruby, я бы начал с сопоставления

%r{
   ([a-zA-Z_][a-zA-Z0-9_]*)   #identifier
   \s*                        #whitespace after the identifier
   \(                         #open paren
     ([^)]*)                  #all arguments as one string
   \)                         #close paren
}x

Затем я бы использовал $2.split(/\s*,\s*/), чтобы разделить аргументы. Я не думаю, что в стандартной библиотеке С++ есть что-то эквивалентное split, однако я думаю, что boost::regex_split может это сделать.

person Ken Bloom    schedule 19.04.2010