C ++: синтаксический анализ с простым регулярным выражением или я должен использовать sscanf? - PullRequest
1 голос
/ 19 апреля 2010

Мне нужно разобрать строку вроде 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 ++ (и, возможно, boost).

Ответы [ 3 ]

2 голосов
/ 19 апреля 2010

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

func1(stuff);

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

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

2 голосов
/ 19 апреля 2010

Как насчет Повышение духа ?

1 голос
/ 19 апреля 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*/), чтобы разделить фрагменты.Я не думаю, что в стандартной библиотеке C ++ есть что-то эквивалентное split, однако я думаю, что boost :: regex_split может это сделать.

...