Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Regular expression to detect semi-colon terminated C++ for & while loops

In my Python application, I need to write a regular expression that matches a C++ for or while loop that has been terminated with a semi-colon (;). For example, it should match this:

for (int i = 0; i < 10; i++); 

... but not this:

for (int i = 0; i < 10; i++) 

This looks trivial at first glance, until you realise that the text between the opening and closing parenthesis may contain other parenthesis, for example:

for (int i = funcA(); i < funcB(); i++); 

I'm using the python.re module. Right now my regular expression looks like this (I've left my comments in so you can understand it easier):

# match any line that begins with a "for" or "while" statement: ^\s*(for|while)\s* \(  # match the initial opening parenthesis     # Now make a named group 'balanced' which matches a balanced substring.     (?P<balanced>         # A balanced substring is either something that is not a parenthesis:         [^()]         | # …or a parenthesised string:         \( # A parenthesised string begins with an opening parenthesis             (?P=balanced)* # …followed by a sequence of balanced substrings         \) # …and ends with a closing parenthesis     )*  # Look for a sequence of balanced substrings \)  # Finally, the outer closing parenthesis. # must end with a semi-colon to match: \s*;\s* 

This works perfectly for all the above cases, but it breaks as soon as you try and make the third part of the for loop contain a function, like so:

for (int i = 0; i < 10; doSomethingTo(i)); 

I think it breaks because as soon as you put some text between the opening and closing parenthesis, the "balanced" group matches that contained text, and thus the (?P=balanced) part doesn't work any more since it won't match (due to the fact that the text inside the parenthesis is different).

In my Python code I'm using the VERBOSE and MULTILINE flags, and creating the regular expression like so:

REGEX_STR = r"""# match any line that begins with a "for" or "while" statement: ^\s*(for|while)\s* \(  # match the initial opening parenthesis     # Now make a named group 'balanced' which matches     # a balanced substring.     (?P<balanced>         # A balanced substring is either something that is not a parenthesis:         [^()]         | # …or a parenthesised string:         \( # A parenthesised string begins with an opening parenthesis             (?P=balanced)* # …followed by a sequence of balanced substrings         \) # …and ends with a closing parenthesis     )*  # Look for a sequence of balanced substrings \)  # Finally, the outer closing parenthesis. # must end with a semi-colon to match: \s*;\s*"""  REGEX_OBJ = re.compile(REGEX_STR, re.MULTILINE| re.VERBOSE) 

Can anyone suggest an improvement to this regular expression? It's getting too complicated for me to get my head around.

like image 559
Thomi Avatar asked Feb 07 '09 20:02

Thomi


People also ask

What is Colon used for in regex?

A colon has no special meaning in Regular Expressions, it just matches a literal colon.

What can be matched using (*) in a regular expression?

A regular expression followed by an asterisk ( * ) matches zero or more occurrences of the regular expression. If there is any choice, the first matching string in a line is used.

Is regex available in C?

It is used in every programming language like C++, Java, and Python.

What will the \$ regular expression match?

\$ will help to find the character "$" available in the content based on the expression flags assigned to the regular expression. Say for example: \$: only find the single "$" in a content \$/g: find the "$" globally available in content.


1 Answers

You could write a little, very simple routine that does it, without using a regular expression:

  • Set a position counter pos so that is points to just before the opening bracket after your for or while.
  • Set an open brackets counter openBr to 0.
  • Now keep incrementing pos, reading the characters at the respective positions, and increment openBr when you see an opening bracket, and decrement it when you see a closing bracket. That will increment it once at the beginning, for the first opening bracket in "for (", increment and decrement some more for some brackets in between, and set it back to 0 when your for bracket closes.
  • So, stop when openBr is 0 again.

The stopping positon is your closing bracket of for(...). Now you can check if there is a semicolon following or not.

like image 63
Frank Avatar answered Oct 03 '22 03:10

Frank