It is probably easier to do this with a simple parser. But you can do it with .NET regex, using balancing groups and realizing that if the brackets are removed from the string, you always have a string matched by a simple expression like ^\d+(?:\s+(?:AND|OR)\s+\d+)*\z .
So, all you have to do is use balancing groups to make sure that the brackets are balanced (and are in the right place in the correct form).
Rewriting the expression above a bit:
(?x)^ OPENING \d+ CLOSING (?: \s+(?:AND|OR)\s+ OPENING \d+ CLOSING )* BALANCED \z
( (?x) makes the regex mechanism ignore all spaces and comments in the template, so it can be made more readable.)
Where OPENING matches any number (0 included) of opening brackets:
\s* (?: (?<open> \( ) \s* )*
CLOSING matches any number of closing brackets, while also providing a balanced balancing group:
\s* (?: (?<-open> \) ) \s* )*
and BALANCED performs a balancing check if there are no open brackets closed:
(?(open)(?!))
Providing an expression:
(?x)^ \s* (?: (?<open> \( ) \s* )* \d+ \s* (?: (?<-open> \) ) \s* )* (?: \s+(?:AND|OR)\s+ \s* (?: (?<open> \( ) \s* )* \d+ \s* (?: (?<-open> \) ) \s* )* )* (?(open)(?!)) \z
If you do not want random spaces to remove each \s* .
Example
See the demo at IdeOne . Exit:
matched: '2' matched: '1 AND 2' matched: '12 OR 234' matched: '(1) AND (2)' matched: '(((1)) AND (2))' matched: '1 AND 2 AND 3' matched: '1 AND (2 OR (3 AND 4))' matched: '1 AND (2 OR 3) AND 4' matched: ' ( 1 AND ( 2 OR ( 3 AND 4 ) )' matched: '((1 AND 7) OR 6) AND ((2 AND 5) OR (3 AND 4))' matched: '(1)' matched: '(((1)))' failed: '1 2' failed: '1(2)' failed: '(1)(2)' failed: 'AND' failed: '1 AND' failed: '(1 AND 2' failed: '1 AND 2)' failed: '1 (AND) 2' failed: '(1 AND 2))' failed: '(1) AND 2)' failed: '(1)() AND (2)' failed: '((1 AND 7) OR 6) AND (2 AND 5) OR (3 AND 4))' failed: '((1 AND 7) OR 6) AND ((2 AND 5 OR (3 AND 4))' failed: ''
Qtax
source share