1

I'm trying to capture the first parameter of a method call using a named group in a regular expression.

E.g. given:

.MyMethod(foo);
.MyMethod(foo, bar);
.MyMethod(new MyObject(1, 2), 3);
.MyMethod(new MyObject()).MyChainedMethod();

The pattern should return for the named group:

foo
foo
new MyObject(1, 2)
new MyObject()

I've tried various combinations, but can't match every case, for example the following matches the second and third cases:

\.MyMethod\((?<firstParam>.+)(?=,|\),)
devdigital
  • 34,151
  • 9
  • 98
  • 120

3 Answers3

4

The following should work:

\.MyMethod\((?<firstParam>(?:[^(),]*|\([^)]*\))+)[,)]

Example (and explanation): http://regex101.com/r/aS2uR9

Note that this works for all of your test cases and will also work with chained function calls for the first parameter, however it will not work if your first parameter contains nested calls, for example .MyMethod(new MyObject(SomeOtherMethod())).

You can add support for arbitrarily nested parentheses by replacing the \([^)]*\) portion with the following expression from this answer:

\((?>[^()]+|\((?<Depth>)|\)(?<-Depth>))*(?(Depth)(?!))\)
Community
  • 1
  • 1
Andrew Clark
  • 202,379
  • 35
  • 273
  • 306
2

You can't. Regex can't count brackets so you will be unable to verify if the number of opened brackets matches the closed ones. It's a limitation of regular expressions, since they can't be recursive.

Build your own parsing method and iterate through the string. Maybe:

string Parse(string str) {
  int open = 0;
  bool started = false;
  int begin;
  for(int x = 0; x < str.length && (open > 0 || !started); x++) {
    char tok = str[x];
    if (started && tok == '(') open++;
    if (started && tok == ')') open--;
    if (!started && tok == '(') { started = true; open++; begin = x+1; }
  }
  assert(begin < str.length && open == 0);
  return str.substring(begin, x - begin);
}  
Jean-Bernard Pellerin
  • 12,556
  • 10
  • 57
  • 79
  • 1
    Actually, C# regex can count occurrences of arbitrary patterns. See [here](http://stackoverflow.com/a/14407908/1121833). That's not to say regex is the way to go for this problem though... – Chad Jun 13 '13 at 19:09
1

Inspired by this answer:

\.MyMethod\((?<firstParam>(?:[^(,]|(?<brackets>\()|(?<-brackets>\))|(?(brackets),))*)(?:\)|,)

This is a complex regex and when regexs get this long its time to start looking at dedicated parsing methods such as that provided by Jean-Bernard Pellerin.

How it works:

\.MyMethod\(                        Basic Text Match for .MyMethod(
(?<firstParam>                      Capturing group
       (?:[^(,]                       Not a bracket or comma
       |(?<brackets>\()             Or a opening bracket that is added to the 
                                    brackets capturing group
       |(?<-brackets>\))            Or a closing bracket that removes an item 
                                    from the brackets capturing group (failing
                                    if there are no items to remove)
       |(?(brackets),))*)             Or if we have reached a lower level a comma
(?:\)|,)                           closing bracket or comma
Community
  • 1
  • 1
user1937198
  • 4,987
  • 4
  • 20
  • 31